mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-12 07:11:18 -05:00
Compare commits
20 Commits
install-sh
...
fix-sessio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb26ad0ff5 | ||
|
|
d14ae65eba | ||
|
|
ae3582afa3 | ||
|
|
99aba9cfa1 | ||
|
|
0cf2d8b785 | ||
|
|
1c80721aff | ||
|
|
63a0458307 | ||
|
|
6f7160556f | ||
|
|
7d5d8f3484 | ||
|
|
f24041725b | ||
|
|
5877bfa8a2 | ||
|
|
13ae67b02c | ||
|
|
464c528634 | ||
|
|
564805bf94 | ||
|
|
c33850f213 | ||
|
|
ab9c4d8f23 | ||
|
|
8c725823f7 | ||
|
|
673f606abf | ||
|
|
1e432b717e | ||
|
|
be5d504b97 |
@@ -497,7 +497,8 @@ def main(ctx):
|
||||
|
||||
is_release_pr = (ctx.build.event == "pull_request" and ctx.build.sender == "openclouders" and "🎉 release" in ctx.build.title.lower())
|
||||
if is_release_pr:
|
||||
return licenseCheck(ctx)
|
||||
return checkVersionPlaceholder() + \
|
||||
licenseCheck(ctx)
|
||||
|
||||
build_release_helpers = \
|
||||
readyReleaseGo()
|
||||
@@ -523,7 +524,6 @@ def main(ctx):
|
||||
testPipelines(ctx)
|
||||
|
||||
build_release_pipelines = \
|
||||
checkVersionPlaceholder() + \
|
||||
dockerReleases(ctx) + \
|
||||
binaryReleases(ctx)
|
||||
|
||||
@@ -1835,7 +1835,7 @@ def checkVersionPlaceholder():
|
||||
},
|
||||
],
|
||||
"when": [
|
||||
event["tag"],
|
||||
event["pull_request"],
|
||||
],
|
||||
}]
|
||||
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## [5.0.2](https://github.com/opencloud-eu/opencloud/releases/tag/v5.0.2) - 2026-02-05
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
@AlexAndBear, @ScharfViktor, @flimmy, @individual-it, @rhafer, @saw-jan
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- [full-ci] reva-bump-2.42.3 [[#2276](https://github.com/opencloud-eu/opencloud/pull/2276)]
|
||||
|
||||
### ✅ Tests
|
||||
|
||||
- adapt test for #514 [[#2255](https://github.com/opencloud-eu/opencloud/pull/2255)]
|
||||
- api-test: upload-rename-download file with back slash [[#2239](https://github.com/opencloud-eu/opencloud/pull/2239)]
|
||||
- [full-ci][tests-only] test: add hook failures to the test failures list [[#2041](https://github.com/opencloud-eu/opencloud/pull/2041)]
|
||||
|
||||
### 📚 Documentation
|
||||
|
||||
- docs(proxy): Clarify PROXY_OIDC_USERINFO_CACHE_TTL value [[#2256](https://github.com/opencloud-eu/opencloud/pull/2256)]
|
||||
|
||||
### 📦️ Dependencies
|
||||
|
||||
- [full-ci] reva-bump-2.42.2 [[#2270](https://github.com/opencloud-eu/opencloud/pull/2270)]
|
||||
- build(deps): bump github.com/grpc-ecosystem/grpc-gateway/v2 from 2.27.5 to 2.27.6 [[#2238](https://github.com/opencloud-eu/opencloud/pull/2238)]
|
||||
|
||||
## [5.0.1](https://github.com/opencloud-eu/opencloud/releases/tag/v5.0.1) - 2026-01-28
|
||||
|
||||
### ❤️ Thanks to all contributors! ❤️
|
||||
|
||||
12
Makefile
12
Makefile
@@ -83,7 +83,7 @@ help:
|
||||
@echo "Please use 'make <target>' where <target> is one of the following:"
|
||||
@echo
|
||||
@echo -e "${GREEN}Testing with test suite natively installed:${RESET}\n"
|
||||
@echo -e "${PURPLE}\tdocs: https://opencloud.dev/opencloud/development/testing/#testing-with-test-suite-natively-installed${RESET}\n"
|
||||
@echo -e "${PURPLE}\ttests: https://github.com/opencloud-eu/opencloud/blob/main/tests/README.md#running-test-suite-in-local-environment${RESET}\n"
|
||||
@echo -e "\tmake test-acceptance-api\t\t${BLUE}run API acceptance tests${RESET}"
|
||||
@echo -e "\tmake clean-tests\t\t\t${BLUE}delete API tests framework dependencies${RESET}"
|
||||
@echo
|
||||
@@ -92,17 +92,11 @@ help:
|
||||
@echo -e "${RED}You also should have a look at other available Makefiles:${RESET}"
|
||||
@echo
|
||||
@echo -e "${GREEN}opencloud:${RESET}\n"
|
||||
@echo -e "${PURPLE}\tdocs: https://opencloud.dev/opencloud/development/build/${RESET}\n"
|
||||
@echo -e "${PURPLE}\tdocs: https://github.com/opencloud-eu/opencloud/blob/main/README.md#build-opencloud${RESET}\n"
|
||||
@echo -e "\tsee ./opencloud/Makefile"
|
||||
@echo -e "\tor run ${YELLOW}make -C opencloud help${RESET}"
|
||||
@echo
|
||||
@echo -e "${GREEN}Documentation:${RESET}\n"
|
||||
@echo -e "${PURPLE}\tdocs: https://opencloud.dev/opencloud/development/build-docs/${RESET}\n"
|
||||
@echo -e "\tsee ./docs/Makefile"
|
||||
@echo -e "\tor run ${YELLOW}make -C docs help${RESET}"
|
||||
@echo
|
||||
@echo -e "${GREEN}Testing with test suite in docker:${RESET}\n"
|
||||
@echo -e "${PURPLE}\tdocs: https://opencloud.dev/opencloud/development/testing/#testing-with-test-suite-in-docker${RESET}\n"
|
||||
@echo -e "${PURPLE}\ttests: https://github.com/opencloud-eu/opencloud/blob/main/tests/README.md#running-test-suite-in-docker${RESET}\n"
|
||||
@echo -e "\tsee ./tests/acceptance/docker/Makefile"
|
||||
@echo -e "\tor run ${YELLOW}make -C tests/acceptance/docker help${RESET}"
|
||||
@echo
|
||||
|
||||
@@ -24,6 +24,7 @@ var demoTenants = []tenantWithUsers{
|
||||
{
|
||||
tenant: libregraph.EducationSchool{
|
||||
DisplayName: libregraph.PtrString("Famous Coders"),
|
||||
ExternalId: libregraph.PtrString("famouscodersExternalID"),
|
||||
},
|
||||
users: []libregraph.EducationUser{
|
||||
{
|
||||
@@ -41,6 +42,7 @@ var demoTenants = []tenantWithUsers{
|
||||
{
|
||||
tenant: libregraph.EducationSchool{
|
||||
DisplayName: libregraph.PtrString("Scientists"),
|
||||
ExternalId: libregraph.PtrString("scientistsExternalID"),
|
||||
},
|
||||
users: []libregraph.EducationUser{
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
- "traefik.http.services.opencloud.loadbalancer.server.port=9200"
|
||||
- "traefik.http.routers.opencloud.${TRAEFIK_SERVICES_TLS_CONFIG}"
|
||||
traefik:
|
||||
image: traefik:v3.3.1
|
||||
image: traefik:v3.6.7
|
||||
# release notes: https://github.com/traefik/traefik/releases
|
||||
networks:
|
||||
opencloud-net:
|
||||
|
||||
@@ -42,4 +42,4 @@ olcObjectClasses: ( openCloudOid:1.2.5 NAME 'openCloudEducationSchool'
|
||||
DESC 'OpenCloud education school objectclass'
|
||||
SUP openCloudObject
|
||||
AUXILIARY
|
||||
MAY ( openCloudEducationSchoolNumber $ openCloudEducationSchoolTerminationTimestamp ) )
|
||||
MAY ( openCloudEducationSchoolNumber $ openCloudEducationSchoolTerminationTimestamp $ openCloudEducationExternalId) )
|
||||
|
||||
24
go.mod
24
go.mod
@@ -18,9 +18,9 @@ require (
|
||||
github.com/davidbyttow/govips/v2 v2.16.0
|
||||
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
|
||||
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
|
||||
github.com/gabriel-vasile/mimetype v1.4.12
|
||||
github.com/gabriel-vasile/mimetype v1.4.13
|
||||
github.com/ggwhite/go-masker v1.1.0
|
||||
github.com/go-chi/chi/v5 v5.2.4
|
||||
github.com/go-chi/chi/v5 v5.2.5
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-jose/go-jose/v3 v3.0.4
|
||||
github.com/go-ldap/ldap/v3 v3.4.12
|
||||
@@ -64,8 +64,8 @@ require (
|
||||
github.com/onsi/gomega v1.39.1
|
||||
github.com/open-policy-agent/opa v1.12.3
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
|
||||
github.com/opencloud-eu/reva/v2 v2.42.2
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068
|
||||
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45
|
||||
github.com/opensearch-project/opensearch-go/v4 v4.6.0
|
||||
github.com/orcaman/concurrent-map v1.0.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -100,7 +100,7 @@ require (
|
||||
go.opentelemetry.io/contrib/zpages v0.64.0
|
||||
go.opentelemetry.io/otel v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0
|
||||
go.opentelemetry.io/otel/sdk v1.40.0
|
||||
go.opentelemetry.io/otel/trace v1.40.0
|
||||
golang.org/x/crypto v0.47.0
|
||||
@@ -130,7 +130,7 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 // indirect
|
||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
@@ -181,7 +181,7 @@ require (
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/crewjam/httperr v0.2.0 // indirect
|
||||
github.com/crewjam/saml v0.4.14 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deckarep/golang-set v1.8.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||
@@ -208,7 +208,7 @@ require (
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.13.2 // indirect
|
||||
github.com/go-git/go-git/v5 v5.16.5 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
@@ -339,9 +339,9 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/samber/lo v1.51.0 // indirect
|
||||
github.com/samber/slog-common v0.19.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
|
||||
github.com/samber/lo v1.52.0 // indirect
|
||||
github.com/samber/slog-common v0.20.0 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.9.1 // indirect
|
||||
github.com/segmentio/asm v1.2.1 // indirect
|
||||
github.com/segmentio/kafka-go v0.4.50 // indirect
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
@@ -353,7 +353,7 @@ require (
|
||||
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spacewander/go-suffix-tree v0.0.0-20191010040751-0865e368c784 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
|
||||
52
go.sum
52
go.sum
@@ -91,8 +91,8 @@ github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzS
|
||||
github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
|
||||
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
|
||||
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5 h1:uGrrMreGjvAtTBobc0g5IrW1D5ldxDQYe2JW2gggRdg=
|
||||
github.com/RoaringBitmap/roaring/v2 v2.4.5/go.mod h1:FiJcsfkGje/nZBZgCu0ZxCPOKD/hVXDS2dXi7/eUFE0=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
@@ -272,8 +272,8 @@ github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1n
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250908152307-4ca807afe54e h1:fC/BWMVWNFlSbzvSp2xTaH0qpJiq7ScRrOsCzpgi1xI=
|
||||
github.com/cs3org/go-cs3apis v0.0.0-20250908152307-4ca807afe54e/go.mod h1:DedpcqXl193qF/08Y04IO0PpxyyMu8+GrkD6kWK2MEQ=
|
||||
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -327,8 +327,8 @@ github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0o
|
||||
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/egirna/icap v0.0.0-20181108071049-d5ee18bd70bc h1:6IxmRbXV8WXVkcYcTzkU219A3UZeNMX/e6X2sve1wXA=
|
||||
github.com/egirna/icap v0.0.0-20181108071049-d5ee18bd70bc/go.mod h1:FdVN2WHg7zOHhJ7kZQdDorfFhIfqZaHttjAzDDvAXHE=
|
||||
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
|
||||
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/emvi/iso-639-1 v1.1.1 h1:7jrl1Sqw9ZYWmCOaH+cpQotLbGr/khwlLPXlBvE8WXU=
|
||||
@@ -358,8 +358,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
|
||||
github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM=
|
||||
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
|
||||
@@ -381,8 +381,8 @@ github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||
github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4=
|
||||
github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
|
||||
@@ -393,8 +393,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
|
||||
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -967,10 +967,10 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l
|
||||
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
|
||||
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU=
|
||||
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
|
||||
github.com/opencloud-eu/reva/v2 v2.42.2 h1:2v2RXsD9qX3L4vhu3Pl4QEXgx6jANeKz3FSFhZ3oU5E=
|
||||
github.com/opencloud-eu/reva/v2 v2.42.2/go.mod h1:LrMYMcSrH9nvTywiE1ry0i2w38yGLFKUPYxWjvKulBo=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068 h1:i09YEVYbiUBMhxyak93REn/ZJOTRhAN4I3PXp2nCXgU=
|
||||
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260204102724-10bcda1b3068/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
|
||||
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45 h1:MWLmUuf9LmEf4hD7RYFtm6n5DysPdAU+LhnAcJCG1kA=
|
||||
github.com/opencloud-eu/reva/v2 v2.42.4-0.20260210144518-86d357fa7b45/go.mod h1:IJPAAnJlnX7kkC1Kmg2Uq3we95rkU+IMq+3Jbntv2JU=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
|
||||
@@ -1103,12 +1103,12 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
|
||||
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
|
||||
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89G9YI=
|
||||
github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/slog-common v0.20.0 h1:WaLnm/aCvBJSk5nR5aXZTFBaV0B47A+AEaEOiZDeUnc=
|
||||
github.com/samber/slog-common v0.20.0/go.mod h1:+Ozat1jgnnE59UAlmNX1IF3IByHsODnnwf9jUcBZ+m8=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.1 h1:RMOq8XqzfuGx1X0TEIlS9OXbbFmqLY2/wJppghz66YY=
|
||||
github.com/samber/slog-zerolog/v2 v2.9.1/go.mod h1:DQYYve14WgCRN/XnKeHl4266jXK0DgYkYXkfZ4Fp98k=
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
|
||||
@@ -1142,8 +1142,8 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
@@ -1325,8 +1325,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDO
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0 h1:8UPA4IbVZxpsD76ihGOQiFml99GPAEZLohDXvqHdi6U=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.39.0/go.mod h1:MZ1T/+51uIVKlRzGw1Fo46KEWThjlCBZKl2LzY5nv4g=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/opencloud-eu/teams/204053/pt/)\n"
|
||||
|
||||
@@ -16,4 +16,5 @@ type FileEvent struct {
|
||||
type BackchannelLogout struct {
|
||||
UserID string `json:"userid"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
SessionID string `json:"sessionid"`
|
||||
}
|
||||
|
||||
@@ -300,5 +300,6 @@ func backchannelLogoutEvent(e events.BackchannelLogout) (string, []string, Backc
|
||||
return "backchannel-logout", []string{e.Executant.GetOpaqueId()}, BackchannelLogout{
|
||||
UserID: e.Executant.GetOpaqueId(),
|
||||
Timestamp: e.Timestamp.String(),
|
||||
SessionID: e.SessionId,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/libregraph/idm/pkg/ldapdn"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
)
|
||||
|
||||
type educationClassAttributeMap struct {
|
||||
|
||||
@@ -34,6 +34,7 @@ type educationConfig struct {
|
||||
type schoolAttributeMap struct {
|
||||
displayName string
|
||||
schoolNumber string
|
||||
externalId string
|
||||
id string
|
||||
terminationDate string
|
||||
}
|
||||
@@ -48,9 +49,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
errNotSet = errors.New("attribute not set")
|
||||
errSchoolNameExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that name is already present")
|
||||
errSchoolNumberExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that number is already present")
|
||||
errNotSet = errors.New("attribute not set")
|
||||
errSchoolNameExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that name is already present")
|
||||
errSchoolNumberExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that number is already present")
|
||||
errSchoolExternalIdExists = errorcode.New(errorcode.NameAlreadyExists, "A school with that external id is already present")
|
||||
)
|
||||
|
||||
func defaultEducationConfig() educationConfig {
|
||||
@@ -105,6 +107,7 @@ func newSchoolAttributeMap() schoolAttributeMap {
|
||||
return schoolAttributeMap{
|
||||
displayName: "ou",
|
||||
schoolNumber: "openCloudEducationSchoolNumber",
|
||||
externalId: "openCloudEducationExternalId",
|
||||
id: "openCloudUUID",
|
||||
terminationDate: "openCloudEducationSchoolTerminationTimestamp",
|
||||
}
|
||||
@@ -121,11 +124,11 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ
|
||||
// Check that the school number is not already used
|
||||
if school.HasSchoolNumber() {
|
||||
_, err := i.getSchoolByNumber(school.GetSchoolNumber())
|
||||
switch err {
|
||||
case nil:
|
||||
switch {
|
||||
case err == nil:
|
||||
logger.Debug().Err(errSchoolNumberExists).Str("schoolNumber", school.GetSchoolNumber()).Msg("duplicate school number")
|
||||
return nil, errSchoolNumberExists
|
||||
case ErrNotFound:
|
||||
case errors.Is(err, ErrNotFound):
|
||||
break
|
||||
default:
|
||||
logger.Error().Err(err).Str("schoolNumber", school.GetSchoolNumber()).Msg("error looking up school by number")
|
||||
@@ -133,6 +136,21 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the school external id is not already used
|
||||
if school.HasExternalId() {
|
||||
_, err := i.getSchoolByExternalId(school.GetExternalId())
|
||||
switch {
|
||||
case err == nil:
|
||||
logger.Debug().Err(errSchoolExternalIdExists).Str("externalId", school.GetExternalId()).Msg("duplicate school external id")
|
||||
return nil, errSchoolExternalIdExists
|
||||
case errors.Is(err, ErrNotFound):
|
||||
break
|
||||
default:
|
||||
logger.Error().Err(err).Str("externalId", school.GetExternalId()).Msg("error looking up school by external id")
|
||||
return nil, errorcode.New(errorcode.GeneralException, "error looking up school by external id")
|
||||
}
|
||||
}
|
||||
|
||||
attributeTypeAndValue := ldap.AttributeTypeAndValue{
|
||||
Type: i.educationConfig.schoolAttributeMap.displayName,
|
||||
Value: school.GetDisplayName(),
|
||||
@@ -147,6 +165,9 @@ func (i *LDAP) CreateEducationSchool(ctx context.Context, school libregraph.Educ
|
||||
if school.HasSchoolNumber() {
|
||||
ar.Attribute(i.educationConfig.schoolAttributeMap.schoolNumber, []string{school.GetSchoolNumber()})
|
||||
}
|
||||
if school.HasExternalId() {
|
||||
ar.Attribute(i.educationConfig.schoolAttributeMap.externalId, []string{school.GetExternalId()})
|
||||
}
|
||||
if !i.useServerUUID {
|
||||
ar.Attribute(i.educationConfig.schoolAttributeMap.id, []string{uuid.NewString()})
|
||||
}
|
||||
@@ -278,14 +299,14 @@ func (i *LDAP) updateSchoolProperties(ctx context.Context, dn string, currentSch
|
||||
}
|
||||
|
||||
// UpdateEducationSchool updates the supplied school in the identity backend
|
||||
func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrID string, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) {
|
||||
func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrIDOrExternalID string, school libregraph.EducationSchool) (*libregraph.EducationSchool, error) {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("UpdateEducationSchool")
|
||||
if !i.writeEnabled {
|
||||
return nil, ErrReadOnly
|
||||
}
|
||||
|
||||
e, err := i.getSchoolByNumberOrID(numberOrID)
|
||||
e, err := i.getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -308,7 +329,7 @@ func (i *LDAP) UpdateEducationSchool(ctx context.Context, numberOrID string, sch
|
||||
}
|
||||
|
||||
// Read back school from LDAP
|
||||
e, err = i.getSchoolByNumberOrID(i.getID(e))
|
||||
e, err = i.getSchoolByNumberOrIDOrExternalID(i.getID(e))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -322,7 +343,7 @@ func (i *LDAP) DeleteEducationSchool(ctx context.Context, id string) error {
|
||||
if !i.writeEnabled {
|
||||
return ErrReadOnly
|
||||
}
|
||||
e, err := i.getSchoolByNumberOrID(id)
|
||||
e, err := i.getSchoolByNumberOrIDOrExternalID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -337,10 +358,10 @@ func (i *LDAP) DeleteEducationSchool(ctx context.Context, id string) error {
|
||||
}
|
||||
|
||||
// GetEducationSchool implements the EducationBackend interface for the LDAP backend.
|
||||
func (i *LDAP) GetEducationSchool(ctx context.Context, numberOrID string) (*libregraph.EducationSchool, error) {
|
||||
func (i *LDAP) GetEducationSchool(ctx context.Context, numberOrIDOrExternalID string) (*libregraph.EducationSchool, error) {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("GetEducationSchool")
|
||||
e, err := i.getSchoolByNumberOrID(numberOrID)
|
||||
e, err := i.getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -415,11 +436,11 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, schoolNumberOrID str
|
||||
}
|
||||
|
||||
// AddUsersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
||||
func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
|
||||
func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberIDs []string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("AddUsersToEducationSchool")
|
||||
|
||||
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
|
||||
schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -462,11 +483,11 @@ func (i *LDAP) AddUsersToEducationSchool(ctx context.Context, schoolNumberOrID s
|
||||
}
|
||||
|
||||
// RemoveUserFromEducationSchool removes a single member (by ID) from a school
|
||||
func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
|
||||
func (i *LDAP) RemoveUserFromEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberID string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("RemoveUserFromEducationSchool")
|
||||
|
||||
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
|
||||
schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -521,12 +542,12 @@ func (i *LDAP) GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID s
|
||||
}
|
||||
|
||||
func (i *LDAP) getEducationSchoolEntries(
|
||||
schoolNumberOrID, filter, objectClass, baseDN string,
|
||||
schoolNumberOrIDOrExternalID, filter, objectClass, baseDN string,
|
||||
scope int,
|
||||
attributes []string,
|
||||
logger log.Logger,
|
||||
) ([]*ldap.Entry, error) {
|
||||
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
|
||||
schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -563,11 +584,11 @@ func (i *LDAP) getEducationSchoolEntries(
|
||||
}
|
||||
|
||||
// AddClassesToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
||||
func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
|
||||
func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberIDs []string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("AddClassesToEducationSchool")
|
||||
|
||||
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
|
||||
schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -610,11 +631,11 @@ func (i *LDAP) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID
|
||||
}
|
||||
|
||||
// RemoveClassFromEducationSchool removes a single member (by ID) from a school
|
||||
func (i *LDAP) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
|
||||
func (i *LDAP) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrIDOrExternalID string, memberID string) error {
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("RemoveClassFromEducationSchool")
|
||||
|
||||
schoolEntry, err := i.getSchoolByNumberOrID(schoolNumberOrID)
|
||||
schoolEntry, err := i.getSchoolByNumberOrIDOrExternalID(schoolNumberOrIDOrExternalID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -652,14 +673,16 @@ func (i *LDAP) getSchoolByDN(dn string) (*ldap.Entry, error) {
|
||||
return i.getEntryByDN(dn, i.getEducationSchoolAttrTypes(), filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) getSchoolByNumberOrID(numberOrID string) (*ldap.Entry, error) {
|
||||
numberOrID = ldap.EscapeFilter(numberOrID)
|
||||
func (i *LDAP) getSchoolByNumberOrIDOrExternalID(numberOrIDOrExternalID string) (*ldap.Entry, error) {
|
||||
numberOrIDOrExternalID = ldap.EscapeFilter(numberOrIDOrExternalID)
|
||||
filter := fmt.Sprintf(
|
||||
"(|(%s=%s)(%s=%s))",
|
||||
"(|(%s=%s)(%s=%s)(%s=%s))",
|
||||
i.educationConfig.schoolAttributeMap.id,
|
||||
numberOrID,
|
||||
numberOrIDOrExternalID,
|
||||
i.educationConfig.schoolAttributeMap.schoolNumber,
|
||||
numberOrID,
|
||||
numberOrIDOrExternalID,
|
||||
i.educationConfig.schoolAttributeMap.externalId,
|
||||
numberOrIDOrExternalID,
|
||||
)
|
||||
return i.getSchoolByFilter(filter)
|
||||
}
|
||||
@@ -674,6 +697,16 @@ func (i *LDAP) getSchoolByNumber(schoolNumber string) (*ldap.Entry, error) {
|
||||
return i.getSchoolByFilter(filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) getSchoolByExternalId(schoolExternalId string) (*ldap.Entry, error) {
|
||||
schoolExternalId = ldap.EscapeFilter(schoolExternalId)
|
||||
filter := fmt.Sprintf(
|
||||
"(%s=%s)",
|
||||
i.educationConfig.schoolAttributeMap.externalId,
|
||||
schoolExternalId,
|
||||
)
|
||||
return i.getSchoolByFilter(filter)
|
||||
}
|
||||
|
||||
func (i *LDAP) getSchoolByFilter(filter string) (*ldap.Entry, error) {
|
||||
filter = fmt.Sprintf("(&%s(objectClass=%s)%s)",
|
||||
i.educationConfig.schoolFilter,
|
||||
@@ -723,6 +756,8 @@ func (i *LDAP) createSchoolModelFromLDAP(e *ldap.Entry) *libregraph.EducationSch
|
||||
id := i.getID(e)
|
||||
schoolNumber := i.getSchoolNumber(e)
|
||||
|
||||
externalId := i.getExternalId(e)
|
||||
|
||||
t, err := i.getTerminationDate(e)
|
||||
if err != nil && !errors.Is(err, errNotSet) {
|
||||
i.logger.Error().Err(err).Str("dn", e.DN).Msg("Error reading termination date for LDAP entry")
|
||||
@@ -739,6 +774,9 @@ func (i *LDAP) createSchoolModelFromLDAP(e *ldap.Entry) *libregraph.EducationSch
|
||||
if schoolNumber != "" {
|
||||
school.SetSchoolNumber(schoolNumber)
|
||||
}
|
||||
if externalId != "" {
|
||||
school.SetExternalId(externalId)
|
||||
}
|
||||
if t != nil {
|
||||
school.SetTerminationDate(*t)
|
||||
}
|
||||
@@ -750,6 +788,11 @@ func (i *LDAP) getSchoolNumber(e *ldap.Entry) string {
|
||||
return schoolNumber
|
||||
}
|
||||
|
||||
func (i *LDAP) getExternalId(e *ldap.Entry) string {
|
||||
externalId := e.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.externalId)
|
||||
return externalId
|
||||
}
|
||||
|
||||
func (i *LDAP) getID(e *ldap.Entry) string {
|
||||
id := e.GetEqualFoldAttributeValue(i.educationConfig.schoolAttributeMap.id)
|
||||
return id
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/identity/mocks"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
@@ -45,6 +45,7 @@ var schoolEntry = ldap.NewEntry("ou=Test School",
|
||||
"ou": {"Test School"},
|
||||
"openCloudEducationSchoolNumber": {"0123"},
|
||||
"openCloudUUID": {"abcd-defg"},
|
||||
"openCloudEducationExternalId": {"abcd-defg"},
|
||||
})
|
||||
|
||||
var schoolEntry1 = ldap.NewEntry("ou=Test School1",
|
||||
@@ -52,20 +53,22 @@ var schoolEntry1 = ldap.NewEntry("ou=Test School1",
|
||||
"ou": {"Test School1"},
|
||||
"openCloudEducationSchoolNumber": {"0042"},
|
||||
"openCloudUUID": {"hijk-defg"},
|
||||
"openCloudEducationExternalId": {"hijk-defg"},
|
||||
})
|
||||
var schoolEntryWithTermination = ldap.NewEntry("ou=Test School",
|
||||
map[string][]string{
|
||||
"ou": {"Test School"},
|
||||
"openCloudEducationSchoolNumber": {"0123"},
|
||||
"openCloudUUID": {"abcd-defg"},
|
||||
"openCloudEducationExternalId": {"abcd-defg"},
|
||||
"openCloudEducationSchoolTerminationTimestamp": {"20420131120000Z"},
|
||||
})
|
||||
|
||||
var (
|
||||
filterSchoolSearchByIdExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=abcd-defg)(openCloudEducationSchoolNumber=abcd-defg)))"
|
||||
filterSchoolSearchByIdNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=xxxx-xxxx)(openCloudEducationSchoolNumber=xxxx-xxxx)))"
|
||||
filterSchoolSearchByNumberExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=0123)(openCloudEducationSchoolNumber=0123)))"
|
||||
filterSchoolSearchByNumberNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=3210)(openCloudEducationSchoolNumber=3210)))"
|
||||
filterSchoolSearchByIdExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=abcd-defg)(openCloudEducationSchoolNumber=abcd-defg)(openCloudEducationExternalId=abcd-defg)))"
|
||||
filterSchoolSearchByIdNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=xxxx-xxxx)(openCloudEducationSchoolNumber=xxxx-xxxx)(openCloudEducationExternalId=xxxx-xxxx)))"
|
||||
filterSchoolSearchByNumberExisting = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=0123)(openCloudEducationSchoolNumber=0123)(openCloudEducationExternalId=0123)))"
|
||||
filterSchoolSearchByNumberNonexistant = "(&(objectClass=openCloudEducationSchool)(|(openCloudUUID=3210)(openCloudEducationSchoolNumber=3210)(openCloudEducationExternalId=3210)))"
|
||||
)
|
||||
|
||||
func TestCreateEducationSchool(t *testing.T) {
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"
|
||||
|
||||
@@ -14,7 +14,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Radoslaw Posim, 2025\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/opencloud-eu/teams/204053/pl/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/opencloud-eu/teams/204053/pt/)\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Lulufox, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: miguel tapias, 2025\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/opencloud-eu/teams/204053/es/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-23 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-12 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>, 2025\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/opencloud-eu/teams/204053/fi/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/opencloud-eu/teams/204053/pt/)\n"
|
||||
|
||||
@@ -12,7 +12,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Lulufox, 2025\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/opencloud-eu/teams/204053/ru/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-23 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-12 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Jiri Grönroos <jiri.gronroos@iki.fi>, 2025\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/opencloud-eu/teams/204053/fi/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: idoet <idoet@protonmail.ch>, 2025\n"
|
||||
"Language-Team: Indonesian (https://app.transifex.com/opencloud-eu/teams/204053/id/)\n"
|
||||
|
||||
@@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Radoslaw Posim, 2025\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/opencloud-eu/teams/204053/pl/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/opencloud-eu/teams/204053/pt/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Stephan Paternotte <stephan@paternottes.net>, 2025\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/opencloud-eu/teams/204053/nl/)\n"
|
||||
|
||||
@@ -13,7 +13,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Radoslaw Posim, 2025\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/opencloud-eu/teams/204053/pl/)\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-01-22 00:11+0000\n"
|
||||
"POT-Creation-Date: 2026-02-11 00:15+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:17+0000\n"
|
||||
"Last-Translator: Mário Machado, 2025\n"
|
||||
"Language-Team: Portuguese (https://app.transifex.com/opencloud-eu/teams/204053/pt/)\n"
|
||||
|
||||
@@ -153,25 +153,30 @@ func Sanitize(cfg *config.Config) {
|
||||
cfg.Web.Config.OpenIDConnect.MetadataURL = strings.TrimRight(cfg.Web.Config.OpenIDConnect.Authority, "/") + "/.well-known/openid-configuration"
|
||||
}
|
||||
// remove AccountEdit parent if no value is set
|
||||
if cfg.Web.Config.Options.AccountEditLink.Href == "" {
|
||||
if cfg.Web.Config.Options.AccountEditLink != nil &&
|
||||
cfg.Web.Config.Options.AccountEditLink.Href == "" {
|
||||
cfg.Web.Config.Options.AccountEditLink = nil
|
||||
}
|
||||
// remove Editor parent if no value is set
|
||||
if !cfg.Web.Config.Options.Editor.AutosaveEnabled {
|
||||
if cfg.Web.Config.Options.Editor != nil &&
|
||||
!cfg.Web.Config.Options.Editor.AutosaveEnabled {
|
||||
cfg.Web.Config.Options.Editor = nil
|
||||
}
|
||||
// remove FeedbackLink parent if no value is set
|
||||
if cfg.Web.Config.Options.FeedbackLink.Href == "" &&
|
||||
if cfg.Web.Config.Options.FeedbackLink != nil &&
|
||||
cfg.Web.Config.Options.FeedbackLink.Href == "" &&
|
||||
cfg.Web.Config.Options.FeedbackLink.AriaLabel == "" &&
|
||||
cfg.Web.Config.Options.FeedbackLink.Description == "" {
|
||||
cfg.Web.Config.Options.FeedbackLink = nil
|
||||
}
|
||||
// remove Upload parent if no value is set
|
||||
if cfg.Web.Config.Options.Upload.CompanionURL == "" {
|
||||
if cfg.Web.Config.Options.Upload != nil &&
|
||||
cfg.Web.Config.Options.Upload.CompanionURL == "" {
|
||||
cfg.Web.Config.Options.Upload = nil
|
||||
}
|
||||
// remove Embed parent if no value is set
|
||||
if cfg.Web.Config.Options.Embed.Enabled == "" &&
|
||||
if cfg.Web.Config.Options.Embed != nil &&
|
||||
cfg.Web.Config.Options.Embed.Enabled == "" &&
|
||||
cfg.Web.Config.Options.Embed.Target == "" &&
|
||||
cfg.Web.Config.Options.Embed.MessagesOrigin == "" &&
|
||||
cfg.Web.Config.Options.Embed.DelegateAuthentication &&
|
||||
|
||||
@@ -324,13 +324,6 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
|
||||
|
||||
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
|
||||
|
||||
- [coreApiWebdavMove/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L143)
|
||||
- [coreApiWebdavMove/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L36)
|
||||
- [coreApiWebdavMove/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L50)
|
||||
- [coreApiWebdavMove/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L64)
|
||||
|
||||
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
|
||||
|
||||
- [coreApiTrashbin/trashbinDelete.feature:92](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiTrashbin/trashbinDelete.feature#L92)
|
||||
@@ -340,9 +333,9 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavMove/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L100)
|
||||
- [coreApiWebdavMove/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L101)
|
||||
- [coreApiWebdavMove/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L102)
|
||||
- [coreApiWebdavMove/moveFolder.feature:220](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L220)
|
||||
- [coreApiWebdavMove/moveFolder.feature:221](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L221)
|
||||
- [coreApiWebdavMove/moveFolder.feature:222](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L222)
|
||||
- [coreApiWebdavMove/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L217)
|
||||
- [coreApiWebdavMove/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L218)
|
||||
- [coreApiWebdavMove/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L219)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L334)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L337)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L340)
|
||||
@@ -370,24 +363,6 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavPreviews/previews.feature:264](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L264)
|
||||
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
|
||||
|
||||
#### [Renaming a file with a slash in the filename makes the file inaccessible](https://github.com/opencloud-eu/web/issues/1893)
|
||||
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L29)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L42)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L148)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:160](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L160)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L172)
|
||||
|
||||
- [coreApiWebdavMove/moveFile.feature:441](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L441)
|
||||
- [coreApiWebdavMove/moveFile.feature:452](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L452)
|
||||
|
||||
- [coreApiWebdavMove/moveFolder.feature:164](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L164)
|
||||
- [coreApiWebdavMove/moveFolder.feature:179](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L179)
|
||||
|
||||
- [coreApiWebdavOperations/downloadFile.feature:307](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L307)
|
||||
- [coreApiWebdavOperations/downloadFile.feature:313](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L313)
|
||||
- [coreApiWebdavOperations/downloadFile.feature:319](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L319)
|
||||
|
||||
### Won't fix
|
||||
|
||||
Not everything needs to be implemented for opencloud.
|
||||
|
||||
@@ -324,12 +324,6 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:376](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L376)
|
||||
- [coreApiWebdavUploadTUS/uploadToShare.feature:377](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavUploadTUS/uploadToShare.feature#L377)
|
||||
|
||||
#### [Renaming resource to banned name is allowed in spaces webdav](https://github.com/owncloud/ocis/issues/3099)
|
||||
|
||||
- [coreApiWebdavMove/moveFile.feature:143](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L143)
|
||||
- [coreApiWebdavMove/moveFolder.feature:36](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L36)
|
||||
- [coreApiWebdavMove/moveFolder.feature:50](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L50)
|
||||
- [coreApiWebdavMove/moveFolder.feature:64](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L64)
|
||||
|
||||
#### [Trying to delete other user's trashbin item returns 409 for spaces path instead of 404](https://github.com/owncloud/ocis/issues/9791)
|
||||
|
||||
@@ -340,9 +334,9 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavMove/moveFile.feature:100](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L100)
|
||||
- [coreApiWebdavMove/moveFile.feature:101](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L101)
|
||||
- [coreApiWebdavMove/moveFile.feature:102](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L102)
|
||||
- [coreApiWebdavMove/moveFolder.feature:220](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L220)
|
||||
- [coreApiWebdavMove/moveFolder.feature:221](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L221)
|
||||
- [coreApiWebdavMove/moveFolder.feature:222](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L222)
|
||||
- [coreApiWebdavMove/moveFolder.feature:217](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L217)
|
||||
- [coreApiWebdavMove/moveFolder.feature:218](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L218)
|
||||
- [coreApiWebdavMove/moveFolder.feature:219](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L219)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:334](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L334)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:337](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L337)
|
||||
- [coreApiWebdavMove/moveShareOnOpencloud.feature:340](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveShareOnOpencloud.feature#L340)
|
||||
@@ -371,24 +365,6 @@ _ocdav: api compatibility, return correct status code_
|
||||
- [coreApiWebdavPreviews/previews.feature:265](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavPreviews/previews.feature#L265)
|
||||
|
||||
|
||||
#### [Renaming a file with a slash in the filename makes the file inaccessible](https://github.com/opencloud-eu/web/issues/1893)
|
||||
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:29](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L29)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:42](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L42)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:148](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L148)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:160](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L160)
|
||||
- [coreApiWebdavProperties/createFileFolder.feature:172](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavProperties/createFileFolder.feature#L172)
|
||||
|
||||
- [coreApiWebdavMove/moveFile.feature:441](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L441)
|
||||
- [coreApiWebdavMove/moveFile.feature:452](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFile.feature#L452)
|
||||
|
||||
- [coreApiWebdavMove/moveFolder.feature:164](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L164)
|
||||
- [coreApiWebdavMove/moveFolder.feature:179](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavMove/moveFolder.feature#L179)
|
||||
|
||||
- [coreApiWebdavOperations/downloadFile.feature:307](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L307)
|
||||
- [coreApiWebdavOperations/downloadFile.feature:313](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L313)
|
||||
- [coreApiWebdavOperations/downloadFile.feature:319](https://github.com/opencloud-eu/opencloud/blob/main/tests/acceptance/features/coreApiWebdavOperations/downloadFile.feature#L319)
|
||||
|
||||
### Won't fix
|
||||
|
||||
Not everything needs to be implemented for opencloud.
|
||||
|
||||
@@ -134,7 +134,7 @@ Feature: move (rename) file
|
||||
Scenario Outline: rename a file into an invalid filename
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Alice" has uploaded file "filesForUpload/textfile.txt" to "fileToRename.txt"
|
||||
When user "Alice" moves file "/fileToRename.txt" to "/a\\a" using the WebDAV API
|
||||
When user "Alice" moves file "/fileToRename.txt" to "/with\\backslash" using the WebDAV API
|
||||
Then the HTTP status code should be "400"
|
||||
Examples:
|
||||
| dav-path-version |
|
||||
@@ -438,7 +438,6 @@ Feature: move (rename) file
|
||||
| old | "strängé नेपाली.txt" | "testfile.txt" |
|
||||
| old | "file,comma.txt" | "testfile.txt" |
|
||||
| old | " start with space.txt" | "testfile.txt" |
|
||||
| old | "testfile.txt" | "with\backslash" |
|
||||
| new | "testfile.txt" | "'single'quotes.txt" |
|
||||
| new | "testfile.txt" | '"double"quotes.txt' |
|
||||
| new | "testfile.txt" | "strängé नेपाली.txt" |
|
||||
@@ -449,7 +448,6 @@ Feature: move (rename) file
|
||||
| new | "strängé नेपाली.txt" | "testfile.txt" |
|
||||
| new | "file,comma.txt" | "testfile.txt" |
|
||||
| new | " start with space.txt" | "testfile.txt" |
|
||||
| new | "testfile.txt" | "with\backslash" |
|
||||
| spaces | "testfile.txt" | "'single'quotes.txt" |
|
||||
| spaces | "testfile.txt" | '"double"quotes.txt' |
|
||||
| spaces | "testfile.txt" | "strängé नेपाली.txt" |
|
||||
@@ -460,7 +458,6 @@ Feature: move (rename) file
|
||||
| spaces | "strängé नेपाली.txt" | "testfile.txt" |
|
||||
| spaces | "file,comma.txt" | "testfile.txt" |
|
||||
| spaces | " start with space.txt" | "testfile.txt" |
|
||||
| spaces | "testfile.txt" | "with\backslash" |
|
||||
|
||||
|
||||
Scenario Outline: try to rename file to name having white space at the end
|
||||
|
||||
@@ -161,7 +161,6 @@ Feature: move (rename) folder
|
||||
| old | "Sample,Folder,With,Comma" | "testFolder" |
|
||||
| old | " start with space" | "testFolder" |
|
||||
| old | "renamed.part" | "testFolder" |
|
||||
| old | "testFolder" | "with\backslash" |
|
||||
| new | "testFolder" | "'single'quotes" |
|
||||
| new | "testFolder" | '"double"quotes' |
|
||||
| new | "testFolder" | "strängé नेपाली folder" |
|
||||
@@ -176,7 +175,6 @@ Feature: move (rename) folder
|
||||
| new | "Sample,Folder,With,Comma" | "testFolder" |
|
||||
| new | " start with space" | "testFolder" |
|
||||
| new | "renamed.part" | "testFolder" |
|
||||
| new | "testFolder" | "with\backslash" |
|
||||
| spaces | "testFolder" | "'single'quotes" |
|
||||
| spaces | "testFolder" | '"double"quotes' |
|
||||
| spaces | "testFolder" | "strängé नेपाली folder" |
|
||||
@@ -191,7 +189,6 @@ Feature: move (rename) folder
|
||||
| spaces | "Sample,Folder,With,Comma" | "testFolder" |
|
||||
| spaces | " start with space" | "testFolder" |
|
||||
| spaces | "renamed.part" | "testFolder" |
|
||||
| spaces | "testFolder" | "with\backslash" |
|
||||
|
||||
|
||||
Scenario Outline: try to rename folder to name having white space at the end
|
||||
|
||||
@@ -304,18 +304,15 @@ Feature: download file
|
||||
| old | "😀 🤖.txt" |
|
||||
| old | "नेपाली" |
|
||||
| old | "C++ file.cpp" |
|
||||
| old | "with\backslash" |
|
||||
| old | "file #2.txt" |
|
||||
| old | "file ?2.pdf" |
|
||||
| new | "😀 🤖.txt" |
|
||||
| new | "नेपाली" |
|
||||
| new | "C++ file.cpp" |
|
||||
| new | "with\backslash" |
|
||||
| new | "file #2.txt" |
|
||||
| new | "file ?2.pdf" |
|
||||
| spaces | "😀 🤖.txt" |
|
||||
| spaces | "नेपाली" |
|
||||
| spaces | "C++ file.cpp" |
|
||||
| spaces | "with\backslash" |
|
||||
| spaces | "file #2.txt" |
|
||||
| spaces | "file ?2.pdf" |
|
||||
|
||||
@@ -26,7 +26,6 @@ Feature: create files and folder
|
||||
| old | "Sample,comma" |
|
||||
| old | "'single'" |
|
||||
| old | '"double"' |
|
||||
| old | "with\backslash" |
|
||||
| new | "upload" |
|
||||
| new | "strängé folder" |
|
||||
| new | "C++ folder.cpp" |
|
||||
@@ -39,7 +38,6 @@ Feature: create files and folder
|
||||
| new | "'single'" |
|
||||
| new | '"double"' |
|
||||
| new | "नेपाली" |
|
||||
| new | "with\backslash" |
|
||||
| spaces | "upload" |
|
||||
| spaces | "strängé folder" |
|
||||
| spaces | "C++ folder.cpp" |
|
||||
@@ -51,7 +49,6 @@ Feature: create files and folder
|
||||
| spaces | "Sample,comma" |
|
||||
| spaces | "'single'" |
|
||||
| spaces | '"double"' |
|
||||
| spaces | "with\backslash" |
|
||||
|
||||
@smokeTest
|
||||
Scenario Outline: get resourcetype property of a folder
|
||||
@@ -145,7 +142,6 @@ Feature: create files and folder
|
||||
| old | "Sample,comma.txt" |
|
||||
| old | "'single'.txt" |
|
||||
| old | '"double".txt' |
|
||||
| old | "with\backslash" |
|
||||
| new | "upload.txt" |
|
||||
| new | "strängéfile.txt" |
|
||||
| new | "C++ file.cpp" |
|
||||
@@ -157,7 +153,6 @@ Feature: create files and folder
|
||||
| new | "Sample,comma.txt" |
|
||||
| new | "'single'.txt" |
|
||||
| new | '"double".txt' |
|
||||
| new | "with\backslash" |
|
||||
| spaces | "upload.txt" |
|
||||
| spaces | "strängéfile.txt" |
|
||||
| spaces | "C++ file.cpp" |
|
||||
@@ -169,7 +164,6 @@ Feature: create files and folder
|
||||
| spaces | "Sample,comma.txt" |
|
||||
| spaces | "'single'.txt" |
|
||||
| spaces | '"double".txt' |
|
||||
| spaces | "with\backslash" |
|
||||
|
||||
@issue-10339 @issue-9568
|
||||
Scenario Outline: try to create file with '.', '..' and 'empty'
|
||||
@@ -208,10 +202,32 @@ Feature: create files and folder
|
||||
| new | | 400 |
|
||||
| spaces | /. | 400 |
|
||||
| spaces | /.. | 405 |
|
||||
| spaces | /../lorem | 404 |
|
||||
| spaces | /../lorem | 400 |
|
||||
| spaces | | 400 |
|
||||
|
||||
|
||||
Scenario Outline: try to create folder with backslash
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" creates folder "<folder-name>" using the WebDAV API
|
||||
Then the HTTP status code should be "400"
|
||||
Examples:
|
||||
| dav-path-version | folder-name |
|
||||
| old | with\backslash |
|
||||
| new | with\backslash |
|
||||
| spaces | with\backslash |
|
||||
|
||||
|
||||
Scenario Outline: try to create file with backslash
|
||||
Given using <dav-path-version> DAV path
|
||||
When user "Alice" uploads file with content "some text" to "<file-name>" using the WebDAV API
|
||||
Then the HTTP status code should be "400"
|
||||
Examples:
|
||||
| dav-path-version | file-name |
|
||||
| old | with\backslash |
|
||||
| new | with\backslash |
|
||||
| spaces | with\backslash |
|
||||
|
||||
|
||||
Scenario Outline: create a file with dots in the name
|
||||
Given using <dav-path-version> DAV path
|
||||
And user "Alice" uploads file with content "some text" to "<file-name>" using the WebDAV API
|
||||
|
||||
20
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
20
vendor/github.com/ProtonMail/go-crypto/openpgp/errors/errors.go
generated
vendored
@@ -6,6 +6,7 @@
|
||||
package errors // import "github.com/ProtonMail/go-crypto/openpgp/errors"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
@@ -178,3 +179,22 @@ type ErrMalformedMessage string
|
||||
func (dke ErrMalformedMessage) Error() string {
|
||||
return "openpgp: malformed message " + string(dke)
|
||||
}
|
||||
|
||||
// ErrEncryptionKeySelection is returned if encryption key selection fails (v2 API).
|
||||
type ErrEncryptionKeySelection struct {
|
||||
PrimaryKeyId string
|
||||
PrimaryKeyErr error
|
||||
EncSelectionKeyId *string
|
||||
EncSelectionErr error
|
||||
}
|
||||
|
||||
func (eks ErrEncryptionKeySelection) Error() string {
|
||||
prefix := fmt.Sprintf("openpgp: key selection for primary key %s:", eks.PrimaryKeyId)
|
||||
if eks.PrimaryKeyErr != nil {
|
||||
return fmt.Sprintf("%s invalid primary key: %s", prefix, eks.PrimaryKeyErr)
|
||||
}
|
||||
if eks.EncSelectionKeyId != nil {
|
||||
return fmt.Sprintf("%s invalid encryption key %s: %s", prefix, *eks.EncSelectionKeyId, eks.EncSelectionErr)
|
||||
}
|
||||
return fmt.Sprintf("%s no encryption key: %s", prefix, eks.EncSelectionErr)
|
||||
}
|
||||
|
||||
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
12
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/config.go
generated
vendored
@@ -173,6 +173,11 @@ type Config struct {
|
||||
// weaknesses in the hash algo, potentially hindering e.g. some chosen-prefix attacks.
|
||||
// The default behavior, when the config or flag is nil, is to enable the feature.
|
||||
NonDeterministicSignaturesViaNotation *bool
|
||||
|
||||
// InsecureAllowAllKeyFlagsWhenMissing determines how a key without valid key flags is handled.
|
||||
// When set to true, a key without flags is treated as if all flags are enabled.
|
||||
// This behavior is consistent with GPG.
|
||||
InsecureAllowAllKeyFlagsWhenMissing bool
|
||||
}
|
||||
|
||||
func (c *Config) Random() io.Reader {
|
||||
@@ -403,6 +408,13 @@ func (c *Config) RandomizeSignaturesViaNotation() bool {
|
||||
return *c.NonDeterministicSignaturesViaNotation
|
||||
}
|
||||
|
||||
func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureAllowAllKeyFlagsWhenMissing
|
||||
}
|
||||
|
||||
// BoolPointer is a helper function to set a boolean pointer in the Config.
|
||||
// e.g., config.CheckPacketSequence = BoolPointer(true)
|
||||
func BoolPointer(value bool) *bool {
|
||||
|
||||
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
7
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/public_key.go
generated
vendored
@@ -1048,12 +1048,17 @@ func (pk *PublicKey) VerifyDirectKeySignature(sig *Signature) (err error) {
|
||||
// KeyIdString returns the public key's fingerprint in capital hex
|
||||
// (e.g. "6C7EE1B8621CC013").
|
||||
func (pk *PublicKey) KeyIdString() string {
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[12:20])
|
||||
return fmt.Sprintf("%016X", pk.KeyId)
|
||||
}
|
||||
|
||||
// KeyIdShortString returns the short form of public key's fingerprint
|
||||
// in capital hex, as shown by gpg --list-keys (e.g. "621CC013").
|
||||
// This function will return the full key id for v5 and v6 keys
|
||||
// since the short key id is undefined for them.
|
||||
func (pk *PublicKey) KeyIdShortString() string {
|
||||
if pk.Version >= 5 {
|
||||
return pk.KeyIdString()
|
||||
}
|
||||
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
4
vendor/github.com/ProtonMail/go-crypto/openpgp/packet/signature.go
generated
vendored
@@ -1288,7 +1288,9 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
|
||||
if sig.IssuerKeyId != nil && sig.Version == 4 {
|
||||
keyId := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(keyId, *sig.IssuerKeyId)
|
||||
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, true, keyId})
|
||||
// Note: making this critical breaks RPM <=4.16.
|
||||
// See: https://github.com/ProtonMail/go-crypto/issues/263
|
||||
subpackets = append(subpackets, outputSubpacket{true, issuerSubpacket, false, keyId})
|
||||
}
|
||||
// Notation Data
|
||||
for _, notation := range sig.Notations {
|
||||
|
||||
49
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/CHANGELOG.md
generated
vendored
@@ -6,6 +6,51 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased] ##
|
||||
|
||||
## [0.4.1] - 2025-01-28 ##
|
||||
|
||||
### Fixed ###
|
||||
- The restrictions added for `root` paths passed to `SecureJoin` in 0.4.0 was
|
||||
found to be too strict and caused some regressions when folks tried to
|
||||
update, so this restriction has been relaxed to only return an error if the
|
||||
path contains a `..` component. We still recommend users use `filepath.Clean`
|
||||
(and even `filepath.EvalSymlinks`) on the `root` path they are using, but at
|
||||
least you will no longer be punished for "trivial" unclean paths.
|
||||
|
||||
## [0.4.0] - 2025-01-13 ##
|
||||
|
||||
### Breaking ####
|
||||
- `SecureJoin(VFS)` will now return an error if the provided `root` is not a
|
||||
`filepath.Clean`'d path.
|
||||
|
||||
While it is ultimately the responsibility of the caller to ensure the root is
|
||||
a safe path to use, passing a path like `/symlink/..` as a root would result
|
||||
in the `SecureJoin`'d path being placed in `/` even though `/symlink/..`
|
||||
might be a different directory, and so we should more strongly discourage
|
||||
such usage.
|
||||
|
||||
All major users of `securejoin.SecureJoin` already ensure that the paths they
|
||||
provide are safe (and this is ultimately a question of user error), but
|
||||
removing this foot-gun is probably a good idea. Of course, this is
|
||||
necessarily a breaking API change (though we expect no real users to be
|
||||
affected by it).
|
||||
|
||||
Thanks to [Erik Sjölund](https://github.com/eriksjolund), who initially
|
||||
reported this issue as a possible security issue.
|
||||
|
||||
- `MkdirAll` and `MkdirHandle` now take an `os.FileMode`-style mode argument
|
||||
instead of a raw `unix.S_*`-style mode argument, which may cause compile-time
|
||||
type errors depending on how you use `filepath-securejoin`. For most users,
|
||||
there will be no change in behaviour aside from the type change (as the
|
||||
bottom `0o777` bits are the same in both formats, and most users are probably
|
||||
only using those bits).
|
||||
|
||||
However, if you were using `unix.S_ISVTX` to set the sticky bit with
|
||||
`MkdirAll(Handle)` you will need to switch to `os.ModeSticky` otherwise you
|
||||
will get a runtime error with this update. In addition, the error message you
|
||||
will get from passing `unix.S_ISUID` and `unix.S_ISGID` will be different as
|
||||
they are treated as invalid bits now (note that previously passing said bits
|
||||
was also an error).
|
||||
|
||||
## [0.3.6] - 2024-12-17 ##
|
||||
|
||||
### Compatibility ###
|
||||
@@ -193,7 +238,9 @@ This is our first release of `github.com/cyphar/filepath-securejoin`,
|
||||
containing a full implementation with a coverage of 93.5% (the only missing
|
||||
cases are the error cases, which are hard to mocktest at the moment).
|
||||
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...HEAD
|
||||
[Unreleased]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.1...HEAD
|
||||
[0.4.1]: https://github.com/cyphar/filepath-securejoin/compare/v0.4.0...v0.4.1
|
||||
[0.4.0]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.6...v0.4.0
|
||||
[0.3.6]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.5...v0.3.6
|
||||
[0.3.5]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.4...v0.3.5
|
||||
[0.3.4]: https://github.com/cyphar/filepath-securejoin/compare/v0.3.3...v0.3.4
|
||||
|
||||
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
2
vendor/github.com/cyphar/filepath-securejoin/VERSION
generated
vendored
@@ -1 +1 @@
|
||||
0.3.6
|
||||
0.4.1
|
||||
|
||||
49
vendor/github.com/cyphar/filepath-securejoin/join.go
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/join.go
generated
vendored
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
|
||||
// Copyright (C) 2017-2024 SUSE LLC. All rights reserved.
|
||||
// Copyright (C) 2017-2025 SUSE LLC. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -24,6 +24,31 @@ func IsNotExist(err error) bool {
|
||||
return errors.Is(err, os.ErrNotExist) || errors.Is(err, syscall.ENOTDIR) || errors.Is(err, syscall.ENOENT)
|
||||
}
|
||||
|
||||
// errUnsafeRoot is returned if the user provides SecureJoinVFS with a path
|
||||
// that contains ".." components.
|
||||
var errUnsafeRoot = errors.New("root path provided to SecureJoin contains '..' components")
|
||||
|
||||
// stripVolume just gets rid of the Windows volume included in a path. Based on
|
||||
// some godbolt tests, the Go compiler is smart enough to make this a no-op on
|
||||
// Linux.
|
||||
func stripVolume(path string) string {
|
||||
return path[len(filepath.VolumeName(path)):]
|
||||
}
|
||||
|
||||
// hasDotDot checks if the path contains ".." components in a platform-agnostic
|
||||
// way.
|
||||
func hasDotDot(path string) bool {
|
||||
// If we are on Windows, strip any volume letters. It turns out that
|
||||
// C:..\foo may (or may not) be a valid pathname and we need to handle that
|
||||
// leading "..".
|
||||
path = stripVolume(path)
|
||||
// Look for "/../" in the path, but we need to handle leading and trailing
|
||||
// ".."s by adding separators. Doing this with filepath.Separator is ugly
|
||||
// so just convert to Unix-style "/" first.
|
||||
path = filepath.ToSlash(path)
|
||||
return strings.Contains("/"+path+"/", "/../")
|
||||
}
|
||||
|
||||
// SecureJoinVFS joins the two given path components (similar to [filepath.Join]) except
|
||||
// that the returned path is guaranteed to be scoped inside the provided root
|
||||
// path (when evaluated). Any symbolic links in the path are evaluated with the
|
||||
@@ -46,7 +71,22 @@ func IsNotExist(err error) bool {
|
||||
// provided via direct input or when evaluating symlinks. Therefore:
|
||||
//
|
||||
// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
|
||||
//
|
||||
// If the provided root is not [filepath.Clean] then an error will be returned,
|
||||
// as such root paths are bordering on somewhat unsafe and using such paths is
|
||||
// not best practice. We also strongly suggest that any root path is first
|
||||
// fully resolved using [filepath.EvalSymlinks] or otherwise constructed to
|
||||
// avoid containing symlink components. Of course, the root also *must not* be
|
||||
// attacker-controlled.
|
||||
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
|
||||
// The root path must not contain ".." components, otherwise when we join
|
||||
// the subpath we will end up with a weird path. We could work around this
|
||||
// in other ways but users shouldn't be giving us non-lexical root paths in
|
||||
// the first place.
|
||||
if hasDotDot(root) {
|
||||
return "", errUnsafeRoot
|
||||
}
|
||||
|
||||
// Use the os.* VFS implementation if none was specified.
|
||||
if vfs == nil {
|
||||
vfs = osVFS{}
|
||||
@@ -59,9 +99,10 @@ func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
|
||||
linksWalked int
|
||||
)
|
||||
for remainingPath != "" {
|
||||
if v := filepath.VolumeName(remainingPath); v != "" {
|
||||
remainingPath = remainingPath[len(v):]
|
||||
}
|
||||
// On Windows, if we managed to end up at a path referencing a volume,
|
||||
// drop the volume to make sure we don't end up with broken paths or
|
||||
// escaping the root volume.
|
||||
remainingPath = stripVolume(remainingPath)
|
||||
|
||||
// Get the next path component.
|
||||
var part string
|
||||
|
||||
49
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
49
vendor/github.com/cyphar/filepath-securejoin/mkdir_linux.go
generated
vendored
@@ -21,6 +21,33 @@ var (
|
||||
errPossibleAttack = errors.New("possible attack detected")
|
||||
)
|
||||
|
||||
// modePermExt is like os.ModePerm except that it also includes the set[ug]id
|
||||
// and sticky bits.
|
||||
const modePermExt = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky
|
||||
|
||||
//nolint:cyclop // this function needs to handle a lot of cases
|
||||
func toUnixMode(mode os.FileMode) (uint32, error) {
|
||||
sysMode := uint32(mode.Perm())
|
||||
if mode&os.ModeSetuid != 0 {
|
||||
sysMode |= unix.S_ISUID
|
||||
}
|
||||
if mode&os.ModeSetgid != 0 {
|
||||
sysMode |= unix.S_ISGID
|
||||
}
|
||||
if mode&os.ModeSticky != 0 {
|
||||
sysMode |= unix.S_ISVTX
|
||||
}
|
||||
// We don't allow file type bits.
|
||||
if mode&os.ModeType != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): type bits not permitted", errInvalidMode, mode, mode)
|
||||
}
|
||||
// We don't allow other unknown modes.
|
||||
if mode&^modePermExt != 0 || sysMode&unix.S_IFMT != 0 {
|
||||
return 0, fmt.Errorf("%w %+.3o (%s): unknown mode bits", errInvalidMode, mode, mode)
|
||||
}
|
||||
return sysMode, nil
|
||||
}
|
||||
|
||||
// MkdirAllHandle is equivalent to [MkdirAll], except that it is safer to use
|
||||
// in two respects:
|
||||
//
|
||||
@@ -39,17 +66,17 @@ var (
|
||||
// a brand new lookup of unsafePath (such as with [SecureJoin] or openat2) after
|
||||
// doing [MkdirAll]. If you intend to open the directory after creating it, you
|
||||
// should use MkdirAllHandle.
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err error) {
|
||||
// Make sure there are no os.FileMode bits set.
|
||||
if mode&^0o7777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o", errInvalidMode, mode)
|
||||
func MkdirAllHandle(root *os.File, unsafePath string, mode os.FileMode) (_ *os.File, Err error) {
|
||||
unixMode, err := toUnixMode(mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// On Linux, mkdirat(2) (and os.Mkdir) silently ignore the suid and sgid
|
||||
// bits. We could also silently ignore them but since we have very few
|
||||
// users it seems more prudent to return an error so users notice that
|
||||
// these bits will not be set.
|
||||
if mode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir 0o%.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
if unixMode&^0o1777 != 0 {
|
||||
return nil, fmt.Errorf("%w for mkdir %+.3o: suid and sgid are ignored by mkdir", errInvalidMode, mode)
|
||||
}
|
||||
|
||||
// Try to open as much of the path as possible.
|
||||
@@ -104,9 +131,6 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
return nil, fmt.Errorf("%w: yet-to-be-created path %q contains '..' components", unix.ENOENT, remainingPath)
|
||||
}
|
||||
|
||||
// Make sure the mode doesn't have any type bits.
|
||||
mode &^= unix.S_IFMT
|
||||
|
||||
// Create the remaining components.
|
||||
for _, part := range remainingParts {
|
||||
switch part {
|
||||
@@ -123,7 +147,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
// directory at the same time as us. In that case, just continue on as
|
||||
// if we created it (if the created inode is not a directory, the
|
||||
// following open call will fail).
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, uint32(mode)); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
if err := unix.Mkdirat(int(currentDir.Fd()), part, unixMode); err != nil && !errors.Is(err, unix.EEXIST) {
|
||||
err = &os.PathError{Op: "mkdirat", Path: currentDir.Name() + "/" + part, Err: err}
|
||||
// Make the error a bit nicer if the directory is dead.
|
||||
if deadErr := isDeadInode(currentDir); deadErr != nil {
|
||||
@@ -196,10 +220,7 @@ func MkdirAllHandle(root *os.File, unsafePath string, mode int) (_ *os.File, Err
|
||||
// If you plan to open the directory after you have created it or want to use
|
||||
// an open directory handle as the root, you should use [MkdirAllHandle] instead.
|
||||
// This function is a wrapper around [MkdirAllHandle].
|
||||
//
|
||||
// NOTE: The mode argument must be set the unix mode bits (unix.S_I...), not
|
||||
// the Go generic mode bits ([os.FileMode]...).
|
||||
func MkdirAll(root, unsafePath string, mode int) error {
|
||||
func MkdirAll(root, unsafePath string, mode os.FileMode) error {
|
||||
rootDir, err := os.OpenFile(root, unix.O_PATH|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
2
vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go
generated
vendored
2
vendor/github.com/gabriel-vasile/mimetype/internal/json/parser.go
generated
vendored
@@ -63,8 +63,6 @@ type parserState struct {
|
||||
// mainly because the functionality is not needed.
|
||||
currPath [][]byte
|
||||
// firstToken stores the first JSON token encountered in input.
|
||||
// TODO: performance would be better if we would stop parsing as soon
|
||||
// as we see that first token is not what we are interested in.
|
||||
firstToken int
|
||||
// querySatisfied is true if both path and value of any queries passed to
|
||||
// consumeAny are satisfied.
|
||||
|
||||
7
vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go
generated
vendored
7
vendor/github.com/gabriel-vasile/mimetype/internal/magic/audio.go
generated
vendored
@@ -40,9 +40,10 @@ func Voc(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("Creative Voice File"))
|
||||
}
|
||||
|
||||
// M3u matches a Playlist file.
|
||||
func M3u(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("#EXTM3U"))
|
||||
// M3U matches a Playlist file.
|
||||
func M3U(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("#EXTM3U\n")) ||
|
||||
bytes.HasPrefix(raw, []byte("#EXTM3U\r\n"))
|
||||
}
|
||||
|
||||
// AAC matches an Advanced Audio Coding file.
|
||||
|
||||
25
vendor/github.com/gabriel-vasile/mimetype/internal/magic/document.go
generated
vendored
25
vendor/github.com/gabriel-vasile/mimetype/internal/magic/document.go
generated
vendored
@@ -3,6 +3,8 @@ package magic
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/gabriel-vasile/mimetype/internal/scan"
|
||||
)
|
||||
|
||||
// Pdf matches a Portable Document Format file.
|
||||
@@ -98,3 +100,26 @@ func Lotus123(raw []byte, _ uint32) bool {
|
||||
func CHM(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("ITSF\003\000\000\000\x60\000\000\000"))
|
||||
}
|
||||
|
||||
// Inf matches an OS/2 .inf file.
|
||||
func Inf(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("HSP\x01\x9b\x00"))
|
||||
}
|
||||
|
||||
// Hlp matches an OS/2 .hlp file.
|
||||
func Hlp(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte("HSP\x10\x9b\x00"))
|
||||
}
|
||||
|
||||
// FrameMaker matches an Adobe FrameMaker file.
|
||||
func FrameMaker(raw []byte, _ uint32) bool {
|
||||
b := scan.Bytes(raw)
|
||||
if !bytes.HasPrefix(b, []byte("<MakerFile")) &&
|
||||
!bytes.HasPrefix(b, []byte("<MakerDictionary")) &&
|
||||
b.Match([]byte("<BOOKFILE"), scan.IgnoreCase) == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// To avoid plain text false positives.
|
||||
return bytes.IndexByte(b[:min(len(b), 512)], 0x00) != -1
|
||||
}
|
||||
|
||||
74
vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go
generated
vendored
74
vendor/github.com/gabriel-vasile/mimetype/internal/magic/font.go
generated
vendored
@@ -2,6 +2,7 @@ package magic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// Woff matches a Web Open Font Format file.
|
||||
@@ -16,7 +17,11 @@ func Woff2(raw []byte, _ uint32) bool {
|
||||
|
||||
// Otf matches an OpenType font file.
|
||||
func Otf(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte{0x4F, 0x54, 0x54, 0x4F, 0x00})
|
||||
// After OTTO an little endian int16 specifies the number of tables.
|
||||
// Since the number of tables cannot exceed 256, the first byte of the
|
||||
// int16 is always 0. PUID: fmt/520
|
||||
return len(raw) > 48 && bytes.HasPrefix(raw, []byte("OTTO\x00")) &&
|
||||
bytes.Contains(raw[12:48], []byte("CFF "))
|
||||
}
|
||||
|
||||
// Ttf matches a TrueType font file.
|
||||
@@ -24,7 +29,72 @@ func Ttf(raw []byte, limit uint32) bool {
|
||||
if !bytes.HasPrefix(raw, []byte{0x00, 0x01, 0x00, 0x00}) {
|
||||
return false
|
||||
}
|
||||
return !MsAccessAce(raw, limit) && !MsAccessMdb(raw, limit)
|
||||
return hasSFNTTable(raw)
|
||||
}
|
||||
|
||||
func hasSFNTTable(raw []byte) bool {
|
||||
// 49 possible tables as explained below
|
||||
if len(raw) < 16 || binary.BigEndian.Uint16(raw[4:]) >= 49 {
|
||||
return false
|
||||
}
|
||||
|
||||
// libmagic says there are 47 table names in specification, but it seems
|
||||
// they reached 49 in the meantime.
|
||||
// https://github.com/file/file/blob/5184ca2471c0e801c156ee120a90e669fe27b31d/magic/Magdir/fonts#L279
|
||||
// At the same time, the TrueType docs seem misleading:
|
||||
// 1. https://developer.apple.com/fonts/TrueType-Reference-Manual/index.html
|
||||
// 2. https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html
|
||||
// Page 1. has 48 tables. Page 2. has 49 tables. The diff is the gcid table.
|
||||
// Take a permissive approach.
|
||||
possibleTables := []uint32{
|
||||
0x61636e74, // "acnt"
|
||||
0x616e6b72, // "ankr"
|
||||
0x61766172, // "avar"
|
||||
0x62646174, // "bdat"
|
||||
0x62686564, // "bhed"
|
||||
0x626c6f63, // "bloc"
|
||||
0x62736c6e, // "bsln"
|
||||
0x636d6170, // "cmap"
|
||||
0x63766172, // "cvar"
|
||||
0x63767420, // "cvt "
|
||||
0x45425343, // "EBSC"
|
||||
0x66647363, // "fdsc"
|
||||
0x66656174, // "feat"
|
||||
0x666d7478, // "fmtx"
|
||||
0x666f6e64, // "fond"
|
||||
0x6670676d, // "fpgm"
|
||||
0x66766172, // "fvar"
|
||||
0x67617370, // "gasp"
|
||||
0x67636964, // "gcid"
|
||||
0x676c7966, // "glyf"
|
||||
0x67766172, // "gvar"
|
||||
0x68646d78, // "hdmx"
|
||||
0x68656164, // "head"
|
||||
0x68686561, // "hhea"
|
||||
0x686d7478, // "hmtx"
|
||||
0x6876676c, // "hvgl"
|
||||
0x6876706d, // "hvpm"
|
||||
0x6a757374, // "just"
|
||||
0x6b65726e, // "kern"
|
||||
0x6b657278, // "kerx"
|
||||
0x6c636172, // "lcar"
|
||||
0x6c6f6361, // "loca"
|
||||
0x6c746167, // "ltag"
|
||||
0x6d617870, // "maxp"
|
||||
0x6d657461, // "meta"
|
||||
0x6d6f7274, // "mort"
|
||||
0x6d6f7278, // "morx"
|
||||
0x6e616d65, // "name"
|
||||
0x6f706264, // "opbd"
|
||||
0x4f532f32, // "OS/2"
|
||||
}
|
||||
ourTable := binary.BigEndian.Uint32(raw[12:16])
|
||||
for _, t := range possibleTables {
|
||||
if ourTable == t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Eot matches an Embedded OpenType font file.
|
||||
|
||||
29
vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go
generated
vendored
29
vendor/github.com/gabriel-vasile/mimetype/internal/magic/image.go
generated
vendored
@@ -1,6 +1,10 @@
|
||||
package magic
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Png matches a Portable Network Graphics file.
|
||||
// https://www.w3.org/TR/PNG/
|
||||
@@ -42,7 +46,28 @@ func Gif(raw []byte, _ uint32) bool {
|
||||
|
||||
// Bmp matches a bitmap image file.
|
||||
func Bmp(raw []byte, _ uint32) bool {
|
||||
return bytes.HasPrefix(raw, []byte{0x42, 0x4D})
|
||||
if len(raw) < 18 {
|
||||
return false
|
||||
}
|
||||
if raw[0] != 'B' || raw[1] != 'M' {
|
||||
return false
|
||||
}
|
||||
|
||||
bmpFormat := binary.LittleEndian.Uint32(raw[14:])
|
||||
// sourced from libmagic Magdir/images
|
||||
possibleFormats := []uint32{
|
||||
48, // PC bitmap, OS/2 2.x format (DIB header size=48)
|
||||
24, // PC bitmap, OS/2 2.x format (DIB header size=24)
|
||||
16, // PC bitmap, OS/2 2.x format (DIB header size=16)
|
||||
64, // PC bitmap, OS/2 2.x format
|
||||
52, // PC bitmap, Adobe Photoshop
|
||||
56, // PC bitmap, Adobe Photoshop with alpha channel mask
|
||||
40, // PC bitmap, Windows 3.x format
|
||||
124, // PC bitmap, Windows 98/2000 and newer format
|
||||
108, // PC bitmap, Windows 95/NT4 and newer format
|
||||
}
|
||||
|
||||
return slices.Contains(possibleFormats, bmpFormat)
|
||||
}
|
||||
|
||||
// Ps matches a PostScript file.
|
||||
|
||||
8
vendor/github.com/gabriel-vasile/mimetype/internal/magic/meteo.go
generated
vendored
8
vendor/github.com/gabriel-vasile/mimetype/internal/magic/meteo.go
generated
vendored
@@ -10,3 +10,11 @@ func GRIB(raw []byte, _ uint32) bool {
|
||||
bytes.HasPrefix(raw, []byte("GRIB")) &&
|
||||
(raw[7] == 1 || raw[7] == 2)
|
||||
}
|
||||
|
||||
// BUFR matches meteorological data format for storing point or time series data.
|
||||
// https://confluence.ecmwf.int/download/attachments/31064617/ecCodes_BUFR_in_a_nutshell.pdf?version=1&modificationDate=1457000352419&api=v2
|
||||
func BUFR(raw []byte, _ uint32) bool {
|
||||
return len(raw) > 7 &&
|
||||
bytes.HasPrefix(raw, []byte("BUFR")) &&
|
||||
(raw[7] == 0x03 || raw[7] == 0x04)
|
||||
}
|
||||
|
||||
6
vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go
generated
vendored
6
vendor/github.com/gabriel-vasile/mimetype/internal/magic/text.go
generated
vendored
@@ -352,6 +352,9 @@ func GLTF(raw []byte, limit uint32) bool {
|
||||
return jsonHelper(raw, limit, json.QueryGLTF, json.TokObject)
|
||||
}
|
||||
|
||||
// jsonHelper parses raw and tries to match the q query against it. wantToks
|
||||
// ensures we're not wasting time parsing an input that would not pass anyway,
|
||||
// ex: the input is a valid JSON array, but we're looking for a JSON object.
|
||||
func jsonHelper(raw scan.Bytes, limit uint32, q string, wantToks ...int) bool {
|
||||
firstNonWS := raw.FirstNonWS()
|
||||
|
||||
@@ -376,7 +379,7 @@ func jsonHelper(raw scan.Bytes, limit uint32, q string, wantToks ...int) bool {
|
||||
|
||||
// If a section of the file was provided, check if all of it was inspected.
|
||||
// In other words, check that if there was a problem parsing, that problem
|
||||
// occurred at the last byte in the input.
|
||||
// occurred after the last byte in the input.
|
||||
return inspected == lraw && lraw > 0
|
||||
}
|
||||
|
||||
@@ -387,7 +390,6 @@ func NdJSON(raw []byte, limit uint32) bool {
|
||||
lCount, objOrArr := 0, 0
|
||||
|
||||
s := scan.Bytes(raw)
|
||||
s.DropLastLine(limit)
|
||||
var l scan.Bytes
|
||||
for len(s) != 0 {
|
||||
l = s.Line()
|
||||
|
||||
9
vendor/github.com/gabriel-vasile/mimetype/mime.go
generated
vendored
9
vendor/github.com/gabriel-vasile/mimetype/mime.go
generated
vendored
@@ -1,7 +1,7 @@
|
||||
package mimetype
|
||||
|
||||
import (
|
||||
"mime"
|
||||
stdmime "mime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -52,8 +52,8 @@ func (m *MIME) Parent() *MIME {
|
||||
func (m *MIME) Is(expectedMIME string) bool {
|
||||
// Parsing is needed because some detected MIME types contain parameters
|
||||
// that need to be stripped for the comparison.
|
||||
expectedMIME, _, _ = mime.ParseMediaType(expectedMIME)
|
||||
found, _, _ := mime.ParseMediaType(m.mime)
|
||||
expectedMIME, _, _ = stdmime.ParseMediaType(expectedMIME)
|
||||
found, _, _ := stdmime.ParseMediaType(m.mime)
|
||||
|
||||
if expectedMIME == found {
|
||||
return true
|
||||
@@ -118,7 +118,7 @@ func (m *MIME) match(in []byte, readLimit uint32) *MIME {
|
||||
|
||||
// flatten transforms an hierarchy of MIMEs into a slice of MIMEs.
|
||||
func (m *MIME) flatten() []*MIME {
|
||||
out := []*MIME{m}
|
||||
out := []*MIME{m} //nolint:prealloc
|
||||
for _, c := range m.children {
|
||||
out = append(out, c.flatten()...)
|
||||
}
|
||||
@@ -196,6 +196,7 @@ func (m *MIME) lookup(mime string) *MIME {
|
||||
// The sub-format will be detected if all the detectors in the parent chain return true.
|
||||
// The extension should include the leading dot, as in ".html".
|
||||
func (m *MIME) Extend(detector func(raw []byte, limit uint32) bool, mime, extension string, aliases ...string) {
|
||||
mime, _, _ = stdmime.ParseMediaType(mime)
|
||||
c := &MIME{
|
||||
mime: mime,
|
||||
extension: extension,
|
||||
|
||||
8
vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md
generated
vendored
8
vendor/github.com/gabriel-vasile/mimetype/supported_mimes.md
generated
vendored
@@ -1,4 +1,4 @@
|
||||
## 195 Supported MIME types
|
||||
## 199 Supported MIME types
|
||||
This file is automatically generated when running tests. Do not edit manually.
|
||||
|
||||
Extension | MIME type <br> Aliases | Hierarchy
|
||||
@@ -99,7 +99,7 @@ Extension | MIME type <br> Aliases | Hierarchy
|
||||
**.asf** | **video/x-ms-asf** <br> video/asf, video/x-ms-wmv | asf>root
|
||||
**.aac** | **audio/aac** | aac>root
|
||||
**.voc** | **audio/x-unknown** | voc>root
|
||||
**.m3u** | **application/vnd.apple.mpegurl** <br> audio/mpegurl | m3u>root
|
||||
**.m3u** | **application/vnd.apple.mpegurl** <br> audio/mpegurl, application/x-mpegurl | m3u>root
|
||||
**.rmvb** | **application/vnd.rn-realmedia-vbr** | rmvb>root
|
||||
**.gz** | **application/gzip** <br> application/x-gzip, application/x-gunzip, application/gzipped, application/gzip-compressed, application/x-gzip-compressed, gzip/document | gz>root
|
||||
**.class** | **application/x-java-applet** | class>root
|
||||
@@ -154,6 +154,10 @@ Extension | MIME type <br> Aliases | Hierarchy
|
||||
**.dxf** | **image/vnd.dxf** | dxf>root
|
||||
**.grb** | **application/grib** | grb>root
|
||||
**n/a** | **application/zlib** | zlib>root
|
||||
**.inf** | **application/x-os2-inf** | inf>root
|
||||
**.hlp** | **application/x-os2-hlp** | hlp>root
|
||||
**.fm** | **application/vnd.framemaker** | fm>root
|
||||
**.bufr** | **application/bufr** | bufr>root
|
||||
**.txt** | **text/plain** | txt>root
|
||||
**.svg** | **image/svg+xml** | svg>txt>root
|
||||
**.html** | **text/html** | html>txt>root
|
||||
|
||||
10
vendor/github.com/gabriel-vasile/mimetype/tree.go
generated
vendored
10
vendor/github.com/gabriel-vasile/mimetype/tree.go
generated
vendored
@@ -24,7 +24,7 @@ var root = newMIME("application/octet-stream", "",
|
||||
woff2, otf, ttc, eot, wasm, shx, dbf, dcm, rar, djvu, mobi, lit, bpg, cbor,
|
||||
sqlite3, dwg, nes, lnk, macho, qcp, icns, hdr, mrc, mdb, accdb, zstd, cab,
|
||||
rpm, xz, lzip, torrent, cpio, tzif, xcf, pat, gbr, glb, cabIS, jxr, parquet,
|
||||
oneNote, chm, wpd, dxf, grib, zlib,
|
||||
oneNote, chm, wpd, dxf, grib, zlib, inf, hlp, fm, bufr,
|
||||
// Keep text last because it is the slowest check.
|
||||
text,
|
||||
)
|
||||
@@ -174,8 +174,8 @@ var (
|
||||
aMp4 = newMIME("audio/mp4", ".mp4", magic.AMp4).
|
||||
alias("audio/x-mp4a")
|
||||
m4a = newMIME("audio/x-m4a", ".m4a", magic.M4a)
|
||||
m3u = newMIME("application/vnd.apple.mpegurl", ".m3u", magic.M3u).
|
||||
alias("audio/mpegurl")
|
||||
m3u = newMIME("application/vnd.apple.mpegurl", ".m3u", magic.M3U).
|
||||
alias("audio/mpegurl", "application/x-mpegurl")
|
||||
m4v = newMIME("video/x-m4v", ".m4v", magic.M4v)
|
||||
mj2 = newMIME("video/mj2", ".mj2", magic.Mj2)
|
||||
dvb = newMIME("video/vnd.dvb.file", ".dvb", magic.Dvb)
|
||||
@@ -290,4 +290,8 @@ var (
|
||||
rfc822 = newMIME("message/rfc822", ".eml", magic.RFC822)
|
||||
grib = newMIME("application/grib", ".grb", magic.GRIB)
|
||||
zlib = newMIME("application/zlib", "", magic.Zlib)
|
||||
inf = newMIME("application/x-os2-inf", ".inf", magic.Inf)
|
||||
hlp = newMIME("application/x-os2-hlp", ".hlp", magic.Hlp)
|
||||
fm = newMIME("application/vnd.framemaker", ".fm", magic.FrameMaker)
|
||||
bufr = newMIME("application/bufr", ".bufr", magic.BUFR)
|
||||
)
|
||||
|
||||
1
vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
generated
vendored
1
vendor/github.com/go-chi/chi/v5/middleware/route_headers.go
generated
vendored
@@ -79,6 +79,7 @@ func (hr HeaderRouter) Handler(next http.Handler) http.Handler {
|
||||
if len(hr) == 0 {
|
||||
// skip if no routes set
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
// find first matching header route, and continue
|
||||
|
||||
56
vendor/github.com/go-git/go-git/v5/options.go
generated
vendored
56
vendor/github.com/go-git/go-git/v5/options.go
generated
vendored
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/go-crypto/openpgp"
|
||||
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
formatcfg "github.com/go-git/go-git/v5/plumbing/format/config"
|
||||
@@ -72,9 +73,16 @@ type CloneOptions struct {
|
||||
// Tags describe how the tags will be fetched from the remote repository,
|
||||
// by default is AllTags.
|
||||
Tags TagMode
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
// ClientCert is the client certificate to use for mutual TLS authentication
|
||||
// over the HTTPS protocol.
|
||||
ClientCert []byte
|
||||
// ClientKey is the client key to use for mutual TLS authentication over
|
||||
// the HTTPS protocol.
|
||||
ClientKey []byte
|
||||
// CABundle specifies an additional CA bundle to use together with the
|
||||
// system cert pool.
|
||||
CABundle []byte
|
||||
// ProxyOptions provides info required for connecting to a proxy.
|
||||
ProxyOptions transport.ProxyOptions
|
||||
@@ -153,9 +161,16 @@ type PullOptions struct {
|
||||
// Force allows the pull to update a local branch even when the remote
|
||||
// branch does not descend from it.
|
||||
Force bool
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
// ClientCert is the client certificate to use for mutual TLS authentication
|
||||
// over the HTTPS protocol.
|
||||
ClientCert []byte
|
||||
// ClientKey is the client key to use for mutual TLS authentication over
|
||||
// the HTTPS protocol.
|
||||
ClientKey []byte
|
||||
// CABundle specifies an additional CA bundle to use together with the
|
||||
// system cert pool.
|
||||
CABundle []byte
|
||||
// ProxyOptions provides info required for connecting to a proxy.
|
||||
ProxyOptions transport.ProxyOptions
|
||||
@@ -211,9 +226,16 @@ type FetchOptions struct {
|
||||
// Force allows the fetch to update a local branch even when the remote
|
||||
// branch does not descend from it.
|
||||
Force bool
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
// ClientCert is the client certificate to use for mutual TLS authentication
|
||||
// over the HTTPS protocol.
|
||||
ClientCert []byte
|
||||
// ClientKey is the client key to use for mutual TLS authentication over
|
||||
// the HTTPS protocol.
|
||||
ClientKey []byte
|
||||
// CABundle specifies an additional CA bundle to use together with the
|
||||
// system cert pool.
|
||||
CABundle []byte
|
||||
// ProxyOptions provides info required for connecting to a proxy.
|
||||
ProxyOptions transport.ProxyOptions
|
||||
@@ -267,9 +289,16 @@ type PushOptions struct {
|
||||
// Force allows the push to update a remote branch even when the local
|
||||
// branch does not descend from it.
|
||||
Force bool
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
// ClientCert is the client certificate to use for mutual TLS authentication
|
||||
// over the HTTPS protocol.
|
||||
ClientCert []byte
|
||||
// ClientKey is the client key to use for mutual TLS authentication over
|
||||
// the HTTPS protocol.
|
||||
ClientKey []byte
|
||||
// CABundle specifies an additional CA bundle to use together with the
|
||||
// system cert pool.
|
||||
CABundle []byte
|
||||
// RequireRemoteRefs only allows a remote ref to be updated if its current
|
||||
// value is the one specified here.
|
||||
@@ -693,9 +722,16 @@ func (o *CreateTagOptions) loadConfigTagger(r *Repository) error {
|
||||
type ListOptions struct {
|
||||
// Auth credentials, if required, to use with the remote repository.
|
||||
Auth transport.AuthMethod
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CABundle specify additional ca bundle with system cert pool
|
||||
// ClientCert is the client certificate to use for mutual TLS authentication
|
||||
// over the HTTPS protocol.
|
||||
ClientCert []byte
|
||||
// ClientKey is the client key to use for mutual TLS authentication over
|
||||
// the HTTPS protocol.
|
||||
ClientKey []byte
|
||||
// CABundle specifies an additional CA bundle to use together with the
|
||||
// system cert pool.
|
||||
CABundle []byte
|
||||
// PeelingOption defines how peeled objects are handled during a
|
||||
// remote list.
|
||||
|
||||
31
vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go
generated
vendored
31
vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go
generated
vendored
@@ -1,9 +1,11 @@
|
||||
package idxfile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
@@ -25,12 +27,15 @@ const (
|
||||
|
||||
// Decoder reads and decodes idx files from an input stream.
|
||||
type Decoder struct {
|
||||
*bufio.Reader
|
||||
io.Reader
|
||||
h hash.Hash
|
||||
}
|
||||
|
||||
// NewDecoder builds a new idx stream decoder, that reads from r.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{bufio.NewReader(r)}
|
||||
h := hash.New(crypto.SHA1)
|
||||
tr := io.TeeReader(r, h)
|
||||
return &Decoder{tr, h}
|
||||
}
|
||||
|
||||
// Decode reads from the stream and decode the content into the MemoryIndex struct.
|
||||
@@ -45,7 +50,7 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
|
||||
readObjectNames,
|
||||
readCRC32,
|
||||
readOffsets,
|
||||
readChecksums,
|
||||
readPackChecksum,
|
||||
}
|
||||
|
||||
for _, f := range flow {
|
||||
@@ -54,11 +59,21 @@ func (d *Decoder) Decode(idx *MemoryIndex) error {
|
||||
}
|
||||
}
|
||||
|
||||
actual := d.h.Sum(nil)
|
||||
if err := readIdxChecksum(idx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(actual, idx.IdxChecksum[:]) {
|
||||
return fmt.Errorf("%w: checksum mismatch: %q instead of %q",
|
||||
ErrMalformedIdxFile, hex.EncodeToString(idx.IdxChecksum[:]), hex.EncodeToString(actual))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHeader(r io.Reader) error {
|
||||
var h = make([]byte, 4)
|
||||
h := make([]byte, 4)
|
||||
if _, err := io.ReadFull(r, h); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -165,11 +180,15 @@ func readOffsets(idx *MemoryIndex, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readChecksums(idx *MemoryIndex, r io.Reader) error {
|
||||
func readPackChecksum(idx *MemoryIndex, r io.Reader) error {
|
||||
if _, err := io.ReadFull(r, idx.PackfileChecksum[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readIdxChecksum(idx *MemoryIndex, r io.Reader) error {
|
||||
if _, err := io.ReadFull(r, idx.IdxChecksum[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
48
vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go
generated
vendored
48
vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/idxfile.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
encbin "encoding/binary"
|
||||
|
||||
@@ -57,8 +58,9 @@ type MemoryIndex struct {
|
||||
PackfileChecksum [hash.Size]byte
|
||||
IdxChecksum [hash.Size]byte
|
||||
|
||||
offsetHash map[int64]plumbing.Hash
|
||||
offsetHashIsFull bool
|
||||
offsetHash map[int64]plumbing.Hash
|
||||
offsetBuildOnce sync.Once
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var _ Index = (*MemoryIndex)(nil)
|
||||
@@ -126,13 +128,13 @@ func (idx *MemoryIndex) FindOffset(h plumbing.Hash) (int64, error) {
|
||||
|
||||
offset := idx.getOffset(k, i)
|
||||
|
||||
if !idx.offsetHashIsFull {
|
||||
// Save the offset for reverse lookup
|
||||
if idx.offsetHash == nil {
|
||||
idx.offsetHash = make(map[int64]plumbing.Hash)
|
||||
}
|
||||
idx.offsetHash[int64(offset)] = h
|
||||
// Save the offset for reverse lookup
|
||||
idx.mu.Lock()
|
||||
if idx.offsetHash == nil {
|
||||
idx.offsetHash = make(map[int64]plumbing.Hash)
|
||||
}
|
||||
idx.offsetHash[int64(offset)] = h
|
||||
idx.mu.Unlock()
|
||||
|
||||
return int64(offset), nil
|
||||
}
|
||||
@@ -173,20 +175,17 @@ func (idx *MemoryIndex) FindHash(o int64) (plumbing.Hash, error) {
|
||||
var hash plumbing.Hash
|
||||
var ok bool
|
||||
|
||||
if idx.offsetHash != nil {
|
||||
if hash, ok = idx.offsetHash[o]; ok {
|
||||
return hash, nil
|
||||
}
|
||||
var genErr error
|
||||
idx.offsetBuildOnce.Do(func() {
|
||||
genErr = idx.genOffsetHash()
|
||||
})
|
||||
if genErr != nil {
|
||||
return plumbing.ZeroHash, genErr
|
||||
}
|
||||
|
||||
// Lazily generate the reverse offset/hash map if required.
|
||||
if !idx.offsetHashIsFull || idx.offsetHash == nil {
|
||||
if err := idx.genOffsetHash(); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
hash, ok = idx.offsetHash[o]
|
||||
}
|
||||
idx.mu.RLock()
|
||||
hash, ok = idx.offsetHash[o]
|
||||
idx.mu.RUnlock()
|
||||
|
||||
if !ok {
|
||||
return plumbing.ZeroHash, plumbing.ErrObjectNotFound
|
||||
@@ -202,8 +201,7 @@ func (idx *MemoryIndex) genOffsetHash() error {
|
||||
return err
|
||||
}
|
||||
|
||||
idx.offsetHash = make(map[int64]plumbing.Hash, count)
|
||||
idx.offsetHashIsFull = true
|
||||
offsetHash := make(map[int64]plumbing.Hash, count)
|
||||
|
||||
var hash plumbing.Hash
|
||||
i := uint32(0)
|
||||
@@ -212,11 +210,15 @@ func (idx *MemoryIndex) genOffsetHash() error {
|
||||
for secondLevel := uint32(0); i < fanoutValue; i++ {
|
||||
copy(hash[:], idx.Names[mappedFirstLevel][secondLevel*objectIDLength:])
|
||||
offset := int64(idx.getOffset(mappedFirstLevel, int(secondLevel)))
|
||||
idx.offsetHash[offset] = hash
|
||||
offsetHash[offset] = hash
|
||||
secondLevel++
|
||||
}
|
||||
}
|
||||
|
||||
idx.mu.Lock()
|
||||
idx.offsetHash = offsetHash
|
||||
idx.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go
generated
vendored
2
vendor/github.com/go-git/go-git/v5/plumbing/format/objfile/reader.go
generated
vendored
@@ -30,7 +30,7 @@ type Reader struct {
|
||||
func NewReader(r io.Reader) (*Reader, error) {
|
||||
zlib, err := sync.GetZlibReader(r)
|
||||
if err != nil {
|
||||
return nil, packfile.ErrZLib.AddDetails(err.Error())
|
||||
return nil, packfile.ErrZLib.AddDetails("%s", err.Error())
|
||||
}
|
||||
|
||||
return &Reader{
|
||||
|
||||
36
vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go
generated
vendored
36
vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/parser.go
generated
vendored
@@ -47,7 +47,6 @@ type Parser struct {
|
||||
oi []*objectInfo
|
||||
oiByHash map[plumbing.Hash]*objectInfo
|
||||
oiByOffset map[int64]*objectInfo
|
||||
checksum plumbing.Hash
|
||||
|
||||
cache *cache.BufferLRU
|
||||
// delta content by offset, only used if source is not seekable
|
||||
@@ -133,28 +132,27 @@ func (p *Parser) onFooter(h plumbing.Hash) error {
|
||||
// Parse start decoding phase of the packfile.
|
||||
func (p *Parser) Parse() (plumbing.Hash, error) {
|
||||
if err := p.init(); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
return plumbing.ZeroHash, wrapEOF(err)
|
||||
}
|
||||
|
||||
if err := p.indexObjects(); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
return plumbing.ZeroHash, wrapEOF(err)
|
||||
}
|
||||
|
||||
var err error
|
||||
p.checksum, err = p.scanner.Checksum()
|
||||
checksum, err := p.scanner.Checksum()
|
||||
if err != nil && err != io.EOF {
|
||||
return plumbing.ZeroHash, err
|
||||
return plumbing.ZeroHash, wrapEOF(err)
|
||||
}
|
||||
|
||||
if err := p.resolveDeltas(); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
return plumbing.ZeroHash, wrapEOF(err)
|
||||
}
|
||||
|
||||
if err := p.onFooter(p.checksum); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
if err := p.onFooter(checksum); err != nil {
|
||||
return plumbing.ZeroHash, wrapEOF(err)
|
||||
}
|
||||
|
||||
return p.checksum, nil
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
func (p *Parser) init() error {
|
||||
@@ -218,7 +216,7 @@ func (p *Parser) indexObjects() error {
|
||||
if !ok {
|
||||
// can't find referenced object in this pack file
|
||||
// this must be a "thin" pack.
|
||||
parent = &objectInfo{ //Placeholder parent
|
||||
parent = &objectInfo{ // Placeholder parent
|
||||
SHA1: oh.Reference,
|
||||
ExternalRef: true, // mark as an external reference that must be resolved
|
||||
Type: plumbing.AnyObject,
|
||||
@@ -531,6 +529,13 @@ func (p *Parser) readData(w io.Writer, o *objectInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func wrapEOF(err error) error {
|
||||
if err == io.ErrUnexpectedEOF || err == io.EOF {
|
||||
return fmt.Errorf("%w: %w", ErrMalformedPackFile, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// applyPatchBase applies the patch to target.
|
||||
//
|
||||
// Note that ota will be updated based on the description in resolveObject.
|
||||
@@ -558,15 +563,6 @@ func applyPatchBase(ota *objectInfo, base io.ReaderAt, delta io.Reader, target i
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSHA1(t plumbing.ObjectType, data []byte) (plumbing.Hash, error) {
|
||||
hasher := plumbing.NewHasher(t, int64(len(data)))
|
||||
if _, err := hasher.Write(data); err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
return hasher.Sum(), nil
|
||||
}
|
||||
|
||||
type objectInfo struct {
|
||||
Offset int64
|
||||
Length int64
|
||||
|
||||
45
vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go
generated
vendored
45
vendor/github.com/go-git/go-git/v5/plumbing/format/packfile/scanner.go
generated
vendored
@@ -3,12 +3,15 @@ package packfile
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
gohash "hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/hash"
|
||||
"github.com/go-git/go-git/v5/utils/binary"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/go-git/go-git/v5/utils/sync"
|
||||
@@ -24,6 +27,8 @@ var (
|
||||
ErrUnsupportedVersion = NewError("unsupported packfile version")
|
||||
// ErrSeekNotSupported returned if seek is not support
|
||||
ErrSeekNotSupported = NewError("not seek support")
|
||||
// ErrMalformedPackFile is returned by the parser when the pack file is corrupted.
|
||||
ErrMalformedPackFile = errors.New("malformed PACK file")
|
||||
)
|
||||
|
||||
// ObjectHeader contains the information related to the object, this information
|
||||
@@ -37,8 +42,9 @@ type ObjectHeader struct {
|
||||
}
|
||||
|
||||
type Scanner struct {
|
||||
r *scannerReader
|
||||
crc hash.Hash32
|
||||
r *scannerReader
|
||||
crc gohash.Hash32
|
||||
packHasher hash.Hash
|
||||
|
||||
// pendingObject is used to detect if an object has been read, or still
|
||||
// is waiting to be read
|
||||
@@ -56,10 +62,12 @@ func NewScanner(r io.Reader) *Scanner {
|
||||
_, ok := r.(io.ReadSeeker)
|
||||
|
||||
crc := crc32.NewIEEE()
|
||||
hasher := hash.New(crypto.SHA1)
|
||||
return &Scanner{
|
||||
r: newScannerReader(r, crc),
|
||||
r: newScannerReader(r, io.MultiWriter(crc, hasher)),
|
||||
crc: crc,
|
||||
IsSeekable: ok,
|
||||
packHasher: hasher,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +76,7 @@ func (s *Scanner) Reset(r io.Reader) {
|
||||
|
||||
s.r.Reset(r)
|
||||
s.crc.Reset()
|
||||
s.packHasher.Reset()
|
||||
s.IsSeekable = ok
|
||||
s.pendingObject = nil
|
||||
s.version = 0
|
||||
@@ -114,7 +123,7 @@ func (s *Scanner) Header() (version, objects uint32, err error) {
|
||||
|
||||
// readSignature reads a returns the signature field in the packfile.
|
||||
func (s *Scanner) readSignature() ([]byte, error) {
|
||||
var sig = make([]byte, 4)
|
||||
sig := make([]byte, 4)
|
||||
if _, err := io.ReadFull(s.r, sig); err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
@@ -322,7 +331,6 @@ func (s *Scanner) NextObject(w io.Writer) (written int64, crc32 uint32, err erro
|
||||
func (s *Scanner) ReadObject() (io.ReadCloser, error) {
|
||||
s.pendingObject = nil
|
||||
zr, err := sync.GetZlibReader(s.r)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("zlib reset error: %s", err)
|
||||
}
|
||||
@@ -374,7 +382,18 @@ func (s *Scanner) Checksum() (plumbing.Hash, error) {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
return binary.ReadHash(s.r)
|
||||
s.r.Flush()
|
||||
actual := plumbing.Hash(s.packHasher.Sum(nil))
|
||||
packChecksum, err := binary.ReadHash(s.r)
|
||||
if err != nil {
|
||||
return plumbing.ZeroHash, err
|
||||
}
|
||||
|
||||
if actual != packChecksum {
|
||||
return plumbing.ZeroHash, fmt.Errorf("%w: checksum mismatch: %q instead of %q", ErrMalformedPackFile, packChecksum, actual)
|
||||
}
|
||||
|
||||
return packChecksum, nil
|
||||
}
|
||||
|
||||
// Close reads the reader until io.EOF
|
||||
@@ -401,17 +420,17 @@ func (s *Scanner) Flush() error {
|
||||
// to the crc32 hash writer.
|
||||
type scannerReader struct {
|
||||
reader io.Reader
|
||||
crc io.Writer
|
||||
writer io.Writer
|
||||
rbuf *bufio.Reader
|
||||
wbuf *bufio.Writer
|
||||
offset int64
|
||||
}
|
||||
|
||||
func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
|
||||
func newScannerReader(r io.Reader, w io.Writer) *scannerReader {
|
||||
sr := &scannerReader{
|
||||
rbuf: bufio.NewReader(nil),
|
||||
wbuf: bufio.NewWriterSize(nil, 64),
|
||||
crc: h,
|
||||
rbuf: bufio.NewReader(nil),
|
||||
wbuf: bufio.NewWriterSize(nil, 64),
|
||||
writer: w,
|
||||
}
|
||||
sr.Reset(r)
|
||||
|
||||
@@ -421,7 +440,7 @@ func newScannerReader(r io.Reader, h io.Writer) *scannerReader {
|
||||
func (r *scannerReader) Reset(reader io.Reader) {
|
||||
r.reader = reader
|
||||
r.rbuf.Reset(r.reader)
|
||||
r.wbuf.Reset(r.crc)
|
||||
r.wbuf.Reset(r.writer)
|
||||
|
||||
r.offset = 0
|
||||
if seeker, ok := r.reader.(io.ReadSeeker); ok {
|
||||
|
||||
72
vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go
generated
vendored
72
vendor/github.com/go-git/go-git/v5/plumbing/object/commit.go
generated
vendored
@@ -62,10 +62,55 @@ type Commit struct {
|
||||
ParentHashes []plumbing.Hash
|
||||
// Encoding is the encoding of the commit.
|
||||
Encoding MessageEncoding
|
||||
// List of extra headers of the commit
|
||||
ExtraHeaders []ExtraHeader
|
||||
|
||||
s storer.EncodedObjectStorer
|
||||
}
|
||||
|
||||
// ExtraHeader holds any non-standard header
|
||||
type ExtraHeader struct {
|
||||
// Header name
|
||||
Key string
|
||||
// Value of the header
|
||||
Value string
|
||||
}
|
||||
|
||||
// Implement fmt.Formatter for ExtraHeader
|
||||
func (h ExtraHeader) Format(f fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
fmt.Fprintf(f, "ExtraHeader{Key: %v, Value: %v}", h.Key, h.Value)
|
||||
default:
|
||||
fmt.Fprintf(f, "%s", h.Key)
|
||||
if len(h.Value) > 0 {
|
||||
fmt.Fprint(f, " ")
|
||||
// Content may be spread on multiple lines, if so we need to
|
||||
// prepend each of them with a space for "continuation".
|
||||
value := strings.TrimSuffix(h.Value, "\n")
|
||||
lines := strings.Split(value, "\n")
|
||||
fmt.Fprint(f, strings.Join(lines, "\n "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse an extra header and indicate whether it may be continue on the next line
|
||||
func parseExtraHeader(line []byte) (ExtraHeader, bool) {
|
||||
split := bytes.SplitN(line, []byte{' '}, 2)
|
||||
|
||||
out := ExtraHeader {
|
||||
Key: string(bytes.TrimRight(split[0], "\n")),
|
||||
Value: "",
|
||||
}
|
||||
|
||||
if len(split) == 2 {
|
||||
out.Value += string(split[1])
|
||||
return out, true
|
||||
} else {
|
||||
return out, false
|
||||
}
|
||||
}
|
||||
|
||||
// GetCommit gets a commit from an object storer and decodes it.
|
||||
func GetCommit(s storer.EncodedObjectStorer, h plumbing.Hash) (*Commit, error) {
|
||||
o, err := s.EncodedObject(plumbing.CommitObject, h)
|
||||
@@ -204,6 +249,7 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
var mergetag bool
|
||||
var pgpsig bool
|
||||
var msgbuf bytes.Buffer
|
||||
var extraheader *ExtraHeader = nil
|
||||
for {
|
||||
line, err := r.ReadBytes('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
@@ -230,7 +276,19 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
if extraheader != nil {
|
||||
if len(line) > 0 && line[0] == ' ' {
|
||||
extraheader.Value += string(line[1:])
|
||||
continue
|
||||
} else {
|
||||
extraheader.Value = strings.TrimRight(extraheader.Value, "\n")
|
||||
c.ExtraHeaders = append(c.ExtraHeaders, *extraheader)
|
||||
extraheader = nil
|
||||
}
|
||||
}
|
||||
|
||||
if !message {
|
||||
original_line := line
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
message = true
|
||||
@@ -261,6 +319,13 @@ func (c *Commit) Decode(o plumbing.EncodedObject) (err error) {
|
||||
case headerpgp:
|
||||
c.PGPSignature += string(data) + "\n"
|
||||
pgpsig = true
|
||||
default:
|
||||
h, maybecontinued := parseExtraHeader(original_line)
|
||||
if maybecontinued {
|
||||
extraheader = &h
|
||||
} else {
|
||||
c.ExtraHeaders = append(c.ExtraHeaders, h)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
msgbuf.Write(line)
|
||||
@@ -341,6 +406,13 @@ func (c *Commit) encode(o plumbing.EncodedObject, includeSig bool) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, header := range c.ExtraHeaders {
|
||||
|
||||
if _, err = fmt.Fprintf(w, "\n%s", header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.PGPSignature != "" && includeSig {
|
||||
if _, err = fmt.Fprint(w, "\n"+headerpgp+" "); err != nil {
|
||||
return err
|
||||
|
||||
5
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs_decode.go
generated
vendored
5
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/advrefs_decode.go
generated
vendored
@@ -262,9 +262,8 @@ func decodeShallow(p *advRefsDecoder) decoderStateFn {
|
||||
p.line = bytes.TrimPrefix(p.line, shallow)
|
||||
|
||||
if len(p.line) != hashSize {
|
||||
p.error(fmt.Sprintf(
|
||||
"malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
|
||||
len(p.line)))
|
||||
p.error("malformed shallow hash: wrong length, expected 40 bytes, read %d bytes",
|
||||
len(p.line))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/updreq_encode.go
generated
vendored
2
vendor/github.com/go-git/go-git/v5/plumbing/protocol/packp/updreq_encode.go
generated
vendored
@@ -62,7 +62,7 @@ func (req *ReferenceUpdateRequest) encodeCommands(e *pktline.Encoder,
|
||||
}
|
||||
|
||||
for _, cmd := range cmds[1:] {
|
||||
if err := e.Encodef(formatCommand(cmd)); err != nil {
|
||||
if err := e.Encodef("%s", formatCommand(cmd)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
12
vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go
generated
vendored
12
vendor/github.com/go-git/go-git/v5/plumbing/transport/common.go
generated
vendored
@@ -113,9 +113,17 @@ type Endpoint struct {
|
||||
Port int
|
||||
// Path is the repository path.
|
||||
Path string
|
||||
// InsecureSkipTLS skips ssl verify if protocol is https
|
||||
// InsecureSkipTLS skips SSL verification if Protocol is HTTPS.
|
||||
InsecureSkipTLS bool
|
||||
// CaBundle specify additional ca bundle with system cert pool
|
||||
// ClientCert specifies an optional client certificate to use for mutual
|
||||
// TLS authentication if Protocol is HTTPS.
|
||||
ClientCert []byte
|
||||
// ClientKey specifies an optional client key to use for mutual TLS
|
||||
// authentication if Protocol is HTTPS.
|
||||
ClientKey []byte
|
||||
// CaBundle specifies an optional CA bundle to use for SSL verification
|
||||
// if Protocol is HTTPS. The bundle is added in addition to the system
|
||||
// CA bundle.
|
||||
CaBundle []byte
|
||||
// Proxy provides info required for connecting to a proxy.
|
||||
Proxy ProxyOptions
|
||||
|
||||
32
vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go
generated
vendored
32
vendor/github.com/go-git/go-git/v5/plumbing/transport/http/common.go
generated
vendored
@@ -15,12 +15,13 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang/groupcache/lru"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/protocol/packp"
|
||||
"github.com/go-git/go-git/v5/plumbing/protocol/packp/capability"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
"github.com/golang/groupcache/lru"
|
||||
)
|
||||
|
||||
// it requires a bytes.Buffer, because we need to know the length
|
||||
@@ -185,6 +186,18 @@ func transportWithInsecureTLS(transport *http.Transport) {
|
||||
transport.TLSClientConfig.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
func transportWithClientCert(transport *http.Transport, cert, key []byte) error {
|
||||
keyPair, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if transport.TLSClientConfig == nil {
|
||||
transport.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
transport.TLSClientConfig.Certificates = []tls.Certificate{keyPair}
|
||||
return nil
|
||||
}
|
||||
|
||||
func transportWithCABundle(transport *http.Transport, caBundle []byte) error {
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
@@ -206,6 +219,11 @@ func transportWithProxy(transport *http.Transport, proxyURL *url.URL) {
|
||||
}
|
||||
|
||||
func configureTransport(transport *http.Transport, ep *transport.Endpoint) error {
|
||||
if len(ep.ClientCert) > 0 && len(ep.ClientKey) > 0 {
|
||||
if err := transportWithClientCert(transport, ep.ClientCert, ep.ClientKey); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(ep.CaBundle) > 0 {
|
||||
if err := transportWithCABundle(transport, ep.CaBundle); err != nil {
|
||||
return err
|
||||
@@ -230,7 +248,7 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
|
||||
|
||||
// We need to configure the http transport if there are transport specific
|
||||
// options present in the endpoint.
|
||||
if len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
|
||||
if len(ep.ClientKey) > 0 || len(ep.ClientCert) > 0 || len(ep.CaBundle) > 0 || ep.InsecureSkipTLS || ep.Proxy.URL != "" {
|
||||
var transport *http.Transport
|
||||
// if the client wasn't configured to have a cache for transports then just configure
|
||||
// the transport and use it directly, otherwise try to use the cache.
|
||||
@@ -242,9 +260,13 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
|
||||
}
|
||||
|
||||
transport = tr.Clone()
|
||||
configureTransport(transport, ep)
|
||||
if err := configureTransport(transport, ep); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
transportOpts := transportOptions{
|
||||
clientCert: string(ep.ClientCert),
|
||||
clientKey: string(ep.ClientKey),
|
||||
caBundle: string(ep.CaBundle),
|
||||
insecureSkipTLS: ep.InsecureSkipTLS,
|
||||
}
|
||||
@@ -260,7 +282,9 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
|
||||
|
||||
if !found {
|
||||
transport = c.client.Transport.(*http.Transport).Clone()
|
||||
configureTransport(transport, ep)
|
||||
if err := configureTransport(transport, ep); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.addTransport(transportOpts, transport)
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/github.com/go-git/go-git/v5/plumbing/transport/http/transport.go
generated
vendored
6
vendor/github.com/go-git/go-git/v5/plumbing/transport/http/transport.go
generated
vendored
@@ -9,8 +9,10 @@ import (
|
||||
type transportOptions struct {
|
||||
insecureSkipTLS bool
|
||||
// []byte is not comparable.
|
||||
caBundle string
|
||||
proxyURL url.URL
|
||||
clientCert string
|
||||
clientKey string
|
||||
caBundle string
|
||||
proxyURL url.URL
|
||||
}
|
||||
|
||||
func (c *client) addTransport(opts transportOptions, transport *http.Transport) {
|
||||
|
||||
76
vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/auth_method.go
generated
vendored
76
vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/auth_method.go
generated
vendored
@@ -54,7 +54,7 @@ func (a *KeyboardInteractive) String() string {
|
||||
}
|
||||
|
||||
func (a *KeyboardInteractive) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
a.Challenge,
|
||||
@@ -78,7 +78,7 @@ func (a *Password) String() string {
|
||||
}
|
||||
|
||||
func (a *Password) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.Password(a.Password)},
|
||||
})
|
||||
@@ -101,7 +101,7 @@ func (a *PasswordCallback) String() string {
|
||||
}
|
||||
|
||||
func (a *PasswordCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PasswordCallback(a.Callback)},
|
||||
})
|
||||
@@ -150,7 +150,7 @@ func (a *PublicKeys) String() string {
|
||||
}
|
||||
|
||||
func (a *PublicKeys) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeys(a.Signer)},
|
||||
})
|
||||
@@ -211,7 +211,7 @@ func (a *PublicKeysCallback) String() string {
|
||||
}
|
||||
|
||||
func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
return a.SetHostKeyCallback(&ssh.ClientConfig{
|
||||
return a.SetHostKeyCallbackAndAlgorithms(&ssh.ClientConfig{
|
||||
User: a.User,
|
||||
Auth: []ssh.AuthMethod{ssh.PublicKeysCallback(a.Callback)},
|
||||
})
|
||||
@@ -230,11 +230,26 @@ func (a *PublicKeysCallback) ClientConfig() (*ssh.ClientConfig, error) {
|
||||
// ~/.ssh/known_hosts
|
||||
// /etc/ssh/ssh_known_hosts
|
||||
func NewKnownHostsCallback(files ...string) (ssh.HostKeyCallback, error) {
|
||||
kh, err := newKnownHosts(files...)
|
||||
return ssh.HostKeyCallback(kh), err
|
||||
kh, err := NewKnownHostsDb(files...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kh.HostKeyCallback(), nil
|
||||
}
|
||||
|
||||
func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
|
||||
// NewKnownHostsDb returns knownhosts.HostKeyDB based on a file based on a
|
||||
// known_hosts file. http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT
|
||||
//
|
||||
// If list of files is empty, then it will be read from the SSH_KNOWN_HOSTS
|
||||
// environment variable, example:
|
||||
//
|
||||
// /home/foo/custom_known_hosts_file:/etc/custom_known/hosts_file
|
||||
//
|
||||
// If SSH_KNOWN_HOSTS is not set the following file locations will be used:
|
||||
//
|
||||
// ~/.ssh/known_hosts
|
||||
// /etc/ssh/ssh_known_hosts
|
||||
func NewKnownHostsDb(files ...string) (*knownhosts.HostKeyDB, error) {
|
||||
var err error
|
||||
|
||||
if len(files) == 0 {
|
||||
@@ -247,7 +262,7 @@ func newKnownHosts(files ...string) (knownhosts.HostKeyCallback, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return knownhosts.New(files...)
|
||||
return knownhosts.NewDB(files...)
|
||||
}
|
||||
|
||||
func getDefaultKnownHostsFiles() ([]string, error) {
|
||||
@@ -289,25 +304,50 @@ func filterKnownHostsFiles(files ...string) ([]string, error) {
|
||||
}
|
||||
|
||||
// HostKeyCallbackHelper is a helper that provides common functionality to
|
||||
// configure HostKeyCallback into a ssh.ClientConfig.
|
||||
// configure HostKeyCallback and HostKeyAlgorithms into a ssh.ClientConfig.
|
||||
type HostKeyCallbackHelper struct {
|
||||
// HostKeyCallback is the function type used for verifying server keys.
|
||||
// If nil default callback will be create using NewKnownHostsCallback
|
||||
// If nil, a default callback will be created using NewKnownHostsDb
|
||||
// without argument.
|
||||
HostKeyCallback ssh.HostKeyCallback
|
||||
|
||||
// HostKeyAlgorithms is a list of supported host key algorithms that will
|
||||
// be used for host key verification.
|
||||
HostKeyAlgorithms []string
|
||||
|
||||
// fallback allows for injecting the fallback call, which is called
|
||||
// when a HostKeyCallback is not set.
|
||||
fallback func(files ...string) (ssh.HostKeyCallback, error)
|
||||
}
|
||||
|
||||
// SetHostKeyCallback sets the field HostKeyCallback in the given cfg. If
|
||||
// HostKeyCallback is empty a default callback is created using
|
||||
// NewKnownHostsCallback.
|
||||
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
|
||||
var err error
|
||||
// SetHostKeyCallbackAndAlgorithms sets the field HostKeyCallback and HostKeyAlgorithms in the given cfg.
|
||||
// If the host key callback or algorithms is empty it is left empty. It will be handled by the dial method,
|
||||
// falling back to knownhosts.
|
||||
func (m *HostKeyCallbackHelper) SetHostKeyCallbackAndAlgorithms(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
|
||||
if cfg == nil {
|
||||
cfg = &ssh.ClientConfig{}
|
||||
}
|
||||
|
||||
if m.HostKeyCallback == nil {
|
||||
if m.HostKeyCallback, err = NewKnownHostsCallback(); err != nil {
|
||||
return cfg, err
|
||||
if m.fallback == nil {
|
||||
m.fallback = NewKnownHostsCallback
|
||||
}
|
||||
|
||||
hkcb, err := m.fallback()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot create known hosts callback: %w", err)
|
||||
}
|
||||
|
||||
cfg.HostKeyCallback = hkcb
|
||||
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
cfg.HostKeyCallback = m.HostKeyCallback
|
||||
cfg.HostKeyAlgorithms = m.HostKeyAlgorithms
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (m *HostKeyCallbackHelper) SetHostKeyCallback(cfg *ssh.ClientConfig) (*ssh.ClientConfig, error) {
|
||||
return m.SetHostKeyCallbackAndAlgorithms(cfg)
|
||||
}
|
||||
|
||||
17
vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go
generated
vendored
17
vendor/github.com/go-git/go-git/v5/plumbing/transport/ssh/common.go
generated
vendored
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport/internal/common"
|
||||
"github.com/skeema/knownhosts"
|
||||
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -127,17 +126,17 @@ func (c *command) connect() error {
|
||||
}
|
||||
hostWithPort := c.getHostWithPort()
|
||||
if config.HostKeyCallback == nil {
|
||||
kh, err := newKnownHosts()
|
||||
db, err := NewKnownHostsDb()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.HostKeyCallback = kh.HostKeyCallback()
|
||||
config.HostKeyAlgorithms = kh.HostKeyAlgorithms(hostWithPort)
|
||||
} else if len(config.HostKeyAlgorithms) == 0 {
|
||||
// Set the HostKeyAlgorithms based on HostKeyCallback.
|
||||
// For background see https://github.com/go-git/go-git/issues/411 as well as
|
||||
// https://github.com/golang/go/issues/29286 for root cause.
|
||||
config.HostKeyAlgorithms = knownhosts.HostKeyAlgorithms(config.HostKeyCallback, hostWithPort)
|
||||
config.HostKeyCallback = db.HostKeyCallback()
|
||||
config.HostKeyAlgorithms = db.HostKeyAlgorithms(hostWithPort)
|
||||
} else {
|
||||
// If the user gave a custom HostKeyCallback, we do not try to detect host key algorithms
|
||||
// based on knownhosts functionality, as the user may be requesting a FixedKey or using a
|
||||
// different key approval strategy. In that case, the user is responsible for populating
|
||||
// HostKeyAlgorithms appropriately
|
||||
}
|
||||
|
||||
overrideConfig(c.config, config)
|
||||
|
||||
20
vendor/github.com/go-git/go-git/v5/remote.go
generated
vendored
20
vendor/github.com/go-git/go-git/v5/remote.go
generated
vendored
@@ -114,7 +114,7 @@ func (r *Remote) PushContext(ctx context.Context, o *PushOptions) (err error) {
|
||||
o.RemoteURL = r.c.URLs[len(r.c.URLs)-1]
|
||||
}
|
||||
|
||||
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
|
||||
s, err := newSendPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func (r *Remote) fetch(ctx context.Context, o *FetchOptions) (sto storer.Referen
|
||||
o.RemoteURL = r.c.URLs[0]
|
||||
}
|
||||
|
||||
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
|
||||
s, err := newUploadPackSession(o.RemoteURL, o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -532,8 +532,8 @@ func depthChanged(before []plumbing.Hash, s storage.Storer) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
|
||||
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
|
||||
func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.UploadPackSession, error) {
|
||||
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -541,8 +541,8 @@ func newUploadPackSession(url string, auth transport.AuthMethod, insecure bool,
|
||||
return c.NewUploadPackSession(ep, auth)
|
||||
}
|
||||
|
||||
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
|
||||
c, ep, err := newClient(url, insecure, cabundle, proxyOpts)
|
||||
func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.ReceivePackSession, error) {
|
||||
c, ep, err := newClient(url, insecure, clientCert, clientKey, caBundle, proxyOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -550,13 +550,15 @@ func newSendPackSession(url string, auth transport.AuthMethod, insecure bool, ca
|
||||
return c.NewReceivePackSession(ep, auth)
|
||||
}
|
||||
|
||||
func newClient(url string, insecure bool, cabundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
|
||||
func newClient(url string, insecure bool, clientCert, clientKey, caBundle []byte, proxyOpts transport.ProxyOptions) (transport.Transport, *transport.Endpoint, error) {
|
||||
ep, err := transport.NewEndpoint(url)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ep.InsecureSkipTLS = insecure
|
||||
ep.CaBundle = cabundle
|
||||
ep.ClientCert = clientCert
|
||||
ep.ClientKey = clientKey
|
||||
ep.CaBundle = caBundle
|
||||
ep.Proxy = proxyOpts
|
||||
|
||||
c, err := client.NewClient(ep)
|
||||
@@ -1356,7 +1358,7 @@ func (r *Remote) list(ctx context.Context, o *ListOptions) (rfs []*plumbing.Refe
|
||||
return nil, ErrEmptyUrls
|
||||
}
|
||||
|
||||
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.CABundle, o.ProxyOptions)
|
||||
s, err := newUploadPackSession(r.c.URLs[0], o.Auth, o.InsecureSkipTLS, o.ClientCert, o.ClientKey, o.CABundle, o.ProxyOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
3
vendor/github.com/go-git/go-git/v5/repository.go
generated
vendored
3
vendor/github.com/go-git/go-git/v5/repository.go
generated
vendored
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/osfs"
|
||||
"github.com/go-git/go-billy/v5/util"
|
||||
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/internal/path_util"
|
||||
"github.com/go-git/go-git/v5/internal/revision"
|
||||
@@ -930,6 +931,8 @@ func (r *Repository) clone(ctx context.Context, o *CloneOptions) error {
|
||||
Tags: o.Tags,
|
||||
RemoteName: o.RemoteName,
|
||||
InsecureSkipTLS: o.InsecureSkipTLS,
|
||||
ClientCert: o.ClientCert,
|
||||
ClientKey: o.ClientKey,
|
||||
CABundle: o.CABundle,
|
||||
ProxyOptions: o.ProxyOptions,
|
||||
}, o.ReferenceName)
|
||||
|
||||
27
vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
generated
vendored
27
vendor/github.com/go-git/go-git/v5/storage/filesystem/object.go
generated
vendored
@@ -2,6 +2,8 @@ package filesystem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -87,6 +89,11 @@ func (s *ObjectStorage) loadIdxFile(h plumbing.Hash) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(idxf.PackfileChecksum[:], h[:]) {
|
||||
return fmt.Errorf("%w: packfile mismatch: target is %q not %q",
|
||||
idxfile.ErrMalformedIdxFile, hex.EncodeToString(idxf.PackfileChecksum[:]), h.String())
|
||||
}
|
||||
|
||||
s.index[h] = idxf
|
||||
return err
|
||||
}
|
||||
@@ -186,7 +193,8 @@ func (s *ObjectStorage) HasEncodedObject(h plumbing.Hash) (err error) {
|
||||
}
|
||||
|
||||
func (s *ObjectStorage) encodedObjectSizeFromUnpacked(h plumbing.Hash) (
|
||||
size int64, err error) {
|
||||
size int64, err error,
|
||||
) {
|
||||
f, err := s.dir.Object(h)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -274,7 +282,8 @@ func (s *ObjectStorage) storePackfileInCache(hash plumbing.Hash, p *packfile.Pac
|
||||
}
|
||||
|
||||
func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
|
||||
size int64, err error) {
|
||||
size int64, err error,
|
||||
) {
|
||||
if err := s.requireIndex(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -310,7 +319,8 @@ func (s *ObjectStorage) encodedObjectSizeFromPackfile(h plumbing.Hash) (
|
||||
// EncodedObjectSize returns the plaintext size of the given object,
|
||||
// without actually reading the full object data from storage.
|
||||
func (s *ObjectStorage) EncodedObjectSize(h plumbing.Hash) (
|
||||
size int64, err error) {
|
||||
size int64, err error,
|
||||
) {
|
||||
size, err = s.encodedObjectSizeFromUnpacked(h)
|
||||
if err != nil && err != plumbing.ErrObjectNotFound {
|
||||
return 0, err
|
||||
@@ -371,7 +381,8 @@ func (s *ObjectStorage) EncodedObject(t plumbing.ObjectType, h plumbing.Hash) (p
|
||||
// DeltaObject returns the object with the given hash, by searching for
|
||||
// it in the packfile and the git object directories.
|
||||
func (s *ObjectStorage) DeltaObject(t plumbing.ObjectType,
|
||||
h plumbing.Hash) (plumbing.EncodedObject, error) {
|
||||
h plumbing.Hash,
|
||||
) (plumbing.EncodedObject, error) {
|
||||
obj, err := s.getFromUnpacked(h)
|
||||
if err == plumbing.ErrObjectNotFound {
|
||||
obj, err = s.getFromPackfile(h, true)
|
||||
@@ -451,8 +462,8 @@ var copyBufferPool = sync.Pool{
|
||||
// Get returns the object with the given hash, by searching for it in
|
||||
// the packfile.
|
||||
func (s *ObjectStorage) getFromPackfile(h plumbing.Hash, canBeDelta bool) (
|
||||
plumbing.EncodedObject, error) {
|
||||
|
||||
plumbing.EncodedObject, error,
|
||||
) {
|
||||
if err := s.requireIndex(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -509,9 +520,7 @@ func (s *ObjectStorage) decodeDeltaObjectAt(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
base plumbing.Hash
|
||||
)
|
||||
var base plumbing.Hash
|
||||
|
||||
switch header.Type {
|
||||
case plumbing.REFDeltaObject:
|
||||
|
||||
6
vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go
generated
vendored
6
vendor/github.com/go-git/go-git/v5/utils/merkletrie/change.go
generated
vendored
@@ -131,7 +131,9 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
|
||||
}
|
||||
|
||||
if !root.IsDir() {
|
||||
l.Add(ctor(root))
|
||||
if !root.Skip() {
|
||||
l.Add(ctor(root))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -148,7 +150,7 @@ func (l *Changes) addRecursive(root noder.Path, ctor noderToChangeFn) error {
|
||||
}
|
||||
return err
|
||||
}
|
||||
if current.IsDir() {
|
||||
if current.IsDir() || current.Skip() {
|
||||
continue
|
||||
}
|
||||
l.Add(ctor(current))
|
||||
|
||||
43
vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
generated
vendored
43
vendor/github.com/go-git/go-git/v5/utils/merkletrie/difftree.go
generated
vendored
@@ -297,18 +297,16 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
|
||||
case noMoreNoders:
|
||||
return ret, nil
|
||||
case onlyFromRemains:
|
||||
if err = ret.AddRecursiveDelete(from); err != nil {
|
||||
return nil, err
|
||||
if !from.Skip() {
|
||||
if err = ret.AddRecursiveDelete(from); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err = ii.nextFrom(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case onlyToRemains:
|
||||
if to.Skip() {
|
||||
if err = ret.AddRecursiveDelete(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if !to.Skip() {
|
||||
if err = ret.AddRecursiveInsert(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -317,26 +315,25 @@ func DiffTreeContext(ctx context.Context, fromTree, toTree noder.Noder,
|
||||
return nil, err
|
||||
}
|
||||
case bothHaveNodes:
|
||||
if from.Skip() {
|
||||
if err = ret.AddRecursiveDelete(from); err != nil {
|
||||
return nil, err
|
||||
var err error
|
||||
switch {
|
||||
case from.Skip():
|
||||
if from.Name() == to.Name() {
|
||||
err = ii.nextBoth()
|
||||
} else {
|
||||
err = ii.nextFrom()
|
||||
}
|
||||
if err := ii.nextBoth(); err != nil {
|
||||
return nil, err
|
||||
case to.Skip():
|
||||
if from.Name() == to.Name() {
|
||||
err = ii.nextBoth()
|
||||
} else {
|
||||
err = ii.nextTo()
|
||||
}
|
||||
break
|
||||
}
|
||||
if to.Skip() {
|
||||
if err = ret.AddRecursiveDelete(to); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ii.nextBoth(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
default:
|
||||
err = diffNodes(&ret, ii)
|
||||
}
|
||||
|
||||
if err = diffNodes(&ret, ii); err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
|
||||
10
vendor/github.com/go-git/go-git/v5/utils/merkletrie/index/node.go
generated
vendored
10
vendor/github.com/go-git/go-git/v5/utils/merkletrie/index/node.go
generated
vendored
@@ -36,7 +36,15 @@ func NewRootNode(idx *index.Index) noder.Noder {
|
||||
parent := fullpath
|
||||
fullpath = path.Join(fullpath, part)
|
||||
|
||||
if _, ok := m[fullpath]; ok {
|
||||
// It's possible that the first occurrence of subdirectory is skipped.
|
||||
// The parent node can be created with SkipWorktree set to true, but
|
||||
// if any future children do not skip their subtree, the entire lineage
|
||||
// of the tree needs to have this value set to false so that subdirectories
|
||||
// are not ignored.
|
||||
if parentNode, ok := m[fullpath]; ok {
|
||||
if e.SkipWorktree == false {
|
||||
parentNode.skip = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
28
vendor/github.com/go-git/go-git/v5/worktree.go
generated
vendored
28
vendor/github.com/go-git/go-git/v5/worktree.go
generated
vendored
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/go-git/go-billy/v5"
|
||||
"github.com/go-git/go-billy/v5/util"
|
||||
|
||||
"github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/filemode"
|
||||
@@ -79,6 +80,8 @@ func (w *Worktree) PullContext(ctx context.Context, o *PullOptions) error {
|
||||
Progress: o.Progress,
|
||||
Force: o.Force,
|
||||
InsecureSkipTLS: o.InsecureSkipTLS,
|
||||
ClientCert: o.ClientCert,
|
||||
ClientKey: o.ClientKey,
|
||||
CABundle: o.CABundle,
|
||||
ProxyOptions: o.ProxyOptions,
|
||||
})
|
||||
@@ -307,13 +310,20 @@ func (w *Worktree) ResetSparsely(opts *ResetOptions, dirs []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var removedFiles []string
|
||||
if opts.Mode == MixedReset || opts.Mode == MergeReset || opts.Mode == HardReset {
|
||||
if err := w.resetIndex(t, dirs, opts.Files); err != nil {
|
||||
if removedFiles, err = w.resetIndex(t, dirs, opts.Files); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Mode == MergeReset || opts.Mode == HardReset {
|
||||
if opts.Mode == MergeReset && len(removedFiles) > 0 {
|
||||
if err := w.resetWorktree(t, removedFiles); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Mode == HardReset {
|
||||
if err := w.resetWorktree(t, opts.Files); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -362,23 +372,24 @@ func (w *Worktree) Reset(opts *ResetOptions) error {
|
||||
return w.ResetSparsely(opts, nil)
|
||||
}
|
||||
|
||||
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) error {
|
||||
func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]string, error) {
|
||||
idx, err := w.r.Storer.Index()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := newIndexBuilder(idx)
|
||||
|
||||
changes, err := w.diffTreeWithStaging(t, true)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var removedFiles []string
|
||||
for _, ch := range changes {
|
||||
a, err := ch.Action()
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var name string
|
||||
@@ -389,7 +400,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
|
||||
name = ch.To.String()
|
||||
e, err = t.FindEntry(name)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
case merkletrie.Delete:
|
||||
name = ch.From.String()
|
||||
@@ -403,6 +414,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
|
||||
}
|
||||
|
||||
b.Remove(name)
|
||||
removedFiles = append(removedFiles, name)
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
@@ -421,7 +433,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) err
|
||||
idx.SkipUnless(dirs)
|
||||
}
|
||||
|
||||
return w.r.Storer.SetIndex(idx)
|
||||
return removedFiles, w.r.Storer.SetIndex(idx)
|
||||
}
|
||||
|
||||
func inFiles(files []string, v string) bool {
|
||||
|
||||
30
vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go
generated
vendored
30
vendor/github.com/opencloud-eu/libre-graph-api-go/api_education_user.go
generated
vendored
@@ -153,8 +153,19 @@ func (r ApiDeleteEducationUserRequest) Execute() (*http.Response, error) {
|
||||
/*
|
||||
DeleteEducationUser Delete educationUser
|
||||
|
||||
Deletes an education user by their internal ID.
|
||||
|
||||
**To delete by external ID:**
|
||||
If you only have an external ID, you must first retrieve the user's internal ID:
|
||||
1. Call `GET /graph/v1.0/education/users?$filter=externalId eq '{value}'`
|
||||
2. Extract the `id` from the response
|
||||
3. Use that `id` in this DELETE endpoint
|
||||
|
||||
See the [ListEducationUsers](#/educationUser/ListEducationUsers) operation for query details.
|
||||
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@param userId key: id or username of user
|
||||
@param userId key: internal user id (UUID format) or username of user. **Note:** If you only have an external ID, first query the user with `GET /graph/v1.0/education/users?$filter=externalId eq '{value}'` to retrieve the internal ID.
|
||||
@return ApiDeleteEducationUserRequest
|
||||
*/
|
||||
func (a *EducationUserApiService) DeleteEducationUser(ctx context.Context, userId string) ApiDeleteEducationUserRequest {
|
||||
@@ -360,10 +371,17 @@ func (a *EducationUserApiService) GetEducationUserExecute(r ApiGetEducationUserR
|
||||
type ApiListEducationUsersRequest struct {
|
||||
ctx context.Context
|
||||
ApiService *EducationUserApiService
|
||||
filter *string
|
||||
orderby *[]string
|
||||
expand *[]string
|
||||
}
|
||||
|
||||
// Filter items by property values. Supports a subset of OData filter expressions. **Supported filters:** - By external ID: `externalId eq 'ext_12345'`
|
||||
func (r ApiListEducationUsersRequest) Filter(filter string) ApiListEducationUsersRequest {
|
||||
r.filter = &filter
|
||||
return r
|
||||
}
|
||||
|
||||
// Order items by property values
|
||||
func (r ApiListEducationUsersRequest) Orderby(orderby []string) ApiListEducationUsersRequest {
|
||||
r.orderby = &orderby
|
||||
@@ -383,6 +401,13 @@ func (r ApiListEducationUsersRequest) Execute() (*CollectionOfEducationUser, *ht
|
||||
/*
|
||||
ListEducationUsers Get entities from education users
|
||||
|
||||
Retrieves a collection of education users with optional filtering, ordering, and expansion.
|
||||
|
||||
**Filtering by external ID:**
|
||||
Use `$filter` to query users by their external identifier, for example:
|
||||
`$filter=externalId eq 'EX12345'`
|
||||
|
||||
|
||||
@param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background().
|
||||
@return ApiListEducationUsersRequest
|
||||
*/
|
||||
@@ -414,6 +439,9 @@ func (a *EducationUserApiService) ListEducationUsersExecute(r ApiListEducationUs
|
||||
localVarQueryParams := url.Values{}
|
||||
localVarFormParams := url.Values{}
|
||||
|
||||
if r.filter != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "$filter", r.filter, "form", "")
|
||||
}
|
||||
if r.orderby != nil {
|
||||
parameterAddToHeaderOrQuery(localVarQueryParams, "$orderby", r.orderby, "form", "csv")
|
||||
}
|
||||
|
||||
37
vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go
generated
vendored
37
vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_school.go
generated
vendored
@@ -26,6 +26,8 @@ type EducationSchool struct {
|
||||
DisplayName *string `json:"displayName,omitempty"`
|
||||
// School number
|
||||
SchoolNumber *string `json:"schoolNumber,omitempty"`
|
||||
// External identifier of the school
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// Date and time at which the service for this organization is scheduled to be terminated
|
||||
TerminationDate NullableTime `json:"terminationDate,omitempty"`
|
||||
}
|
||||
@@ -143,6 +145,38 @@ func (o *EducationSchool) SetSchoolNumber(v string) {
|
||||
o.SchoolNumber = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *EducationSchool) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *EducationSchool) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *EducationSchool) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *EducationSchool) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetTerminationDate returns the TerminationDate field value if set, zero value otherwise (both if not set or set to explicit null).
|
||||
func (o *EducationSchool) GetTerminationDate() time.Time {
|
||||
if o == nil || IsNil(o.TerminationDate.Get()) {
|
||||
@@ -204,6 +238,9 @@ func (o EducationSchool) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.SchoolNumber) {
|
||||
toSerialize["schoolNumber"] = o.SchoolNumber
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
if o.TerminationDate.IsSet() {
|
||||
toSerialize["terminationDate"] = o.TerminationDate.Get()
|
||||
}
|
||||
|
||||
37
vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go
generated
vendored
37
vendor/github.com/opencloud-eu/libre-graph-api-go/model_education_user.go
generated
vendored
@@ -28,6 +28,8 @@ type EducationUser struct {
|
||||
// A collection of drives available for this user. Read-only.
|
||||
Drives []Drive `json:"drives,omitempty"`
|
||||
Drive *Drive `json:"drive,omitempty"`
|
||||
// An external unique ID for the user. Use it to associate a user in another system, such as a student or employee ID number.
|
||||
ExternalId *string `json:"externalId,omitempty"`
|
||||
// Identities associated with this account.
|
||||
Identities []ObjectIdentity `json:"identities,omitempty"`
|
||||
// The SMTP address for the user, for example, 'jeff@contoso.opencloud.com'. Returned by default.
|
||||
@@ -224,6 +226,38 @@ func (o *EducationUser) SetDrive(v Drive) {
|
||||
o.Drive = &v
|
||||
}
|
||||
|
||||
// GetExternalId returns the ExternalId field value if set, zero value otherwise.
|
||||
func (o *EducationUser) GetExternalId() string {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
var ret string
|
||||
return ret
|
||||
}
|
||||
return *o.ExternalId
|
||||
}
|
||||
|
||||
// GetExternalIdOk returns a tuple with the ExternalId field value if set, nil otherwise
|
||||
// and a boolean to check if the value has been set.
|
||||
func (o *EducationUser) GetExternalIdOk() (*string, bool) {
|
||||
if o == nil || IsNil(o.ExternalId) {
|
||||
return nil, false
|
||||
}
|
||||
return o.ExternalId, true
|
||||
}
|
||||
|
||||
// HasExternalId returns a boolean if a field has been set.
|
||||
func (o *EducationUser) HasExternalId() bool {
|
||||
if o != nil && !IsNil(o.ExternalId) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetExternalId gets a reference to the given string and assigns it to the ExternalId field.
|
||||
func (o *EducationUser) SetExternalId(v string) {
|
||||
o.ExternalId = &v
|
||||
}
|
||||
|
||||
// GetIdentities returns the Identities field value if set, zero value otherwise.
|
||||
func (o *EducationUser) GetIdentities() []ObjectIdentity {
|
||||
if o == nil || IsNil(o.Identities) {
|
||||
@@ -537,6 +571,9 @@ func (o EducationUser) ToMap() (map[string]interface{}, error) {
|
||||
if !IsNil(o.Drive) {
|
||||
toSerialize["drive"] = o.Drive
|
||||
}
|
||||
if !IsNil(o.ExternalId) {
|
||||
toSerialize["externalId"] = o.ExternalId
|
||||
}
|
||||
if !IsNil(o.Identities) {
|
||||
toSerialize["identities"] = o.Identities
|
||||
}
|
||||
|
||||
8
vendor/github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/auth/scope.go
generated
vendored
8
vendor/github.com/opencloud-eu/reva/v2/internal/grpc/interceptors/auth/scope.go
generated
vendored
@@ -21,6 +21,7 @@ package auth
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -283,8 +284,11 @@ func checkIfNestedResource(ctx context.Context, ref *provider.Reference, parent
|
||||
return false, statuspkg.NewErrorFromCode(pathResp.Status.Code, "auth interceptor")
|
||||
}
|
||||
childPath := pathResp.Path
|
||||
|
||||
return strings.HasPrefix(childPath, parentPath), nil
|
||||
rel, err := filepath.Rel(parentPath, childPath)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return !strings.HasPrefix(rel, ".."), nil
|
||||
}
|
||||
|
||||
func extractRefFromListProvidersReq(v *registry.ListStorageProvidersRequest) (*provider.Reference, bool) {
|
||||
|
||||
@@ -97,6 +97,10 @@ func (s *svc) handleSpacesMkCol(w http.ResponseWriter, r *http.Request, spaceID
|
||||
|
||||
sublog := appctx.GetLogger(ctx).With().Str("path", r.URL.Path).Str("spaceid", spaceID).Str("handler", "mkcol").Logger()
|
||||
|
||||
if err := ValidateName(filename(r.URL.Path), s.nameValidators); err != nil {
|
||||
return http.StatusBadRequest, err
|
||||
}
|
||||
|
||||
parentRef, err := spacelookup.MakeStorageSpaceReference(spaceID, path.Dir(r.URL.Path))
|
||||
if err != nil {
|
||||
return http.StatusBadRequest, fmt.Errorf("invalid space id")
|
||||
|
||||
10
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/owncloud/ocdav/move.go
generated
vendored
10
vendor/github.com/opencloud-eu/reva/v2/internal/http/services/owncloud/ocdav/move.go
generated
vendored
@@ -131,6 +131,16 @@ func (s *svc) handleSpacesMove(w http.ResponseWriter, r *http.Request, srcSpaceI
|
||||
|
||||
dstSpaceID, dstRelPath := router.ShiftPath(dst)
|
||||
|
||||
if dstRelPath != "" && dstRelPath != "." && dstRelPath != "/" {
|
||||
err := ValidateDestination(filename(dstRelPath), s.nameValidators)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
b, err := errors.Marshal(http.StatusBadRequest, "destination naming rules", "", "")
|
||||
errors.HandleWebdavError(appctx.GetLogger(ctx), w, b, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dstRef, err := spacelookup.MakeStorageSpaceReference(dstSpaceID, dstRelPath)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
2
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go
generated
vendored
2
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/lookup/lookup.go
generated
vendored
@@ -84,7 +84,7 @@ type Lookup struct {
|
||||
// New returns a new Lookup instance
|
||||
func New(b metadata.Backend, um usermapper.Mapper, o *options.Options, tm node.TimeManager) *Lookup {
|
||||
idHistoryConf := o.Options.IDCache
|
||||
idHistoryConf.Database = o.Options.IDCache.Table + "_history"
|
||||
idHistoryConf.Table = o.Options.IDCache.Table + "_history"
|
||||
idHistoryConf.TTL = 1 * time.Minute
|
||||
|
||||
spaceRootCache, _ := lru.New[string, string](1000)
|
||||
|
||||
24
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/tree.go
generated
vendored
24
vendor/github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/tree/tree.go
generated
vendored
@@ -381,6 +381,15 @@ func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node)
|
||||
newNode.ID = oldNode.ID
|
||||
}
|
||||
|
||||
// rename node
|
||||
err = os.Rename(
|
||||
filepath.Join(oldParent, oldNode.Name),
|
||||
filepath.Join(newParent, newNode.Name),
|
||||
)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "posixfs: could not move child")
|
||||
}
|
||||
|
||||
// update the id cache
|
||||
// invalidate old tree
|
||||
err = t.lookup.IDCache.DeleteByPath(ctx, filepath.Join(oldNode.ParentPath(), oldNode.Name))
|
||||
@@ -391,21 +400,6 @@ func (t *Tree) Move(ctx context.Context, oldNode *node.Node, newNode *node.Node)
|
||||
t.log.Error().Err(err).Str("spaceID", newNode.SpaceID).Str("id", newNode.ID).Str("path", filepath.Join(newNode.ParentPath(), newNode.Name)).Msg("could not cache id")
|
||||
}
|
||||
|
||||
// rename node
|
||||
err = os.Rename(
|
||||
filepath.Join(oldParent, oldNode.Name),
|
||||
filepath.Join(newParent, newNode.Name),
|
||||
)
|
||||
if err != nil {
|
||||
if err := t.lookup.CacheID(ctx, oldNode.SpaceID, oldNode.ID, filepath.Join(oldNode.ParentPath(), oldNode.Name)); err != nil {
|
||||
t.log.Error().Err(err).Str("spaceID", oldNode.SpaceID).Str("id", oldNode.ID).Str("path", filepath.Join(oldNode.ParentPath(), oldNode.Name)).Msg("could not reset cached id after failed move")
|
||||
}
|
||||
if err := t.WarmupIDCache(filepath.Join(oldNode.ParentPath(), oldNode.Name), false, false); err != nil {
|
||||
t.log.Error().Err(err).Str("spaceID", oldNode.SpaceID).Str("id", oldNode.ID).Str("path", filepath.Join(oldNode.ParentPath(), oldNode.Name)).Msg("could not warum cached after failed move")
|
||||
}
|
||||
return errors.Wrap(err, "posixfs: could not move child")
|
||||
}
|
||||
|
||||
// update target parentid and name
|
||||
attribs := node.Attributes{}
|
||||
attribs.SetString(prefixes.ParentidAttr, newNode.ParentID)
|
||||
|
||||
104
vendor/github.com/samber/lo/.golangci.yml
generated
vendored
Normal file
104
vendor/github.com/samber/lo/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
version: "2"
|
||||
run:
|
||||
concurrency: 4
|
||||
# also lint _test.go files
|
||||
tests: true
|
||||
timeout: 5m
|
||||
linters:
|
||||
enable:
|
||||
- govet
|
||||
- staticcheck
|
||||
- unused
|
||||
- errcheck
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- revive
|
||||
- ineffassign
|
||||
- unconvert
|
||||
- goconst
|
||||
# - depguard
|
||||
- prealloc
|
||||
# - dupl
|
||||
- misspell
|
||||
- bodyclose
|
||||
- sqlclosecheck
|
||||
- nilerr
|
||||
- nestif
|
||||
- forcetypeassert
|
||||
- exhaustive
|
||||
- funlen
|
||||
# - wsl_v5
|
||||
- testifylint
|
||||
- whitespace
|
||||
- perfsprint
|
||||
- nolintlint
|
||||
- godot
|
||||
- thelper
|
||||
- tparallel
|
||||
- paralleltest
|
||||
- predeclared
|
||||
|
||||
# disable noisy/controversial ones which you might enable later
|
||||
disable:
|
||||
- lll # line length — handled by gfmt/gofumpt
|
||||
|
||||
settings:
|
||||
dupl:
|
||||
threshold: 20 # lower => stricter (tokens)
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
funlen:
|
||||
lines: 120
|
||||
statements: 80
|
||||
goconst:
|
||||
min-len: 2
|
||||
min-occurrences: 3
|
||||
gocyclo:
|
||||
min-complexity: 15 # strict; lower => stricter
|
||||
wsl_v5:
|
||||
allow-first-in-block: true
|
||||
allow-whole-block: false
|
||||
branch-max-lines: 2
|
||||
testifylint:
|
||||
disable:
|
||||
- require-error
|
||||
- float-compare
|
||||
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- examples$
|
||||
rules:
|
||||
- linters:
|
||||
- revive
|
||||
text: "^unused-parameter:"
|
||||
- linters:
|
||||
- revive
|
||||
text: "^package-comments:"
|
||||
- linters:
|
||||
- errcheck
|
||||
text: "Error return value of `.*\\.Body\\.Close` is not checked"
|
||||
# linters disabled in tests
|
||||
- linters:
|
||||
- dupl
|
||||
- goconst
|
||||
- funlen
|
||||
path: "_test\\.go$"
|
||||
|
||||
issues:
|
||||
max-issues-per-linter: 0 # 0 = unlimited (we want ALL issues)
|
||||
max-same-issues: 100
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
settings:
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
24
vendor/github.com/samber/lo/Makefile
generated
vendored
24
vendor/github.com/samber/lo/Makefile
generated
vendored
@@ -3,20 +3,19 @@ build:
|
||||
go build -v ./...
|
||||
|
||||
test:
|
||||
go test -race -v ./...
|
||||
go test -race ./...
|
||||
watch-test:
|
||||
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
|
||||
reflex -t 50ms -s -- sh -c 'gotest -race ./...'
|
||||
|
||||
bench:
|
||||
go test -benchmem -count 3 -bench ./...
|
||||
go test -v -run=^Benchmark -benchmem -count 3 -bench ./...
|
||||
watch-bench:
|
||||
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
|
||||
reflex -t 50ms -s -- sh -c 'go test -v -run=^Benchmark -benchmem -count 3 -bench ./...'
|
||||
|
||||
coverage:
|
||||
go test -v -coverprofile=cover.out -covermode=atomic ./...
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
# tools
|
||||
tools:
|
||||
go install github.com/cespare/reflex@latest
|
||||
go install github.com/rakyll/gotest@latest
|
||||
@@ -25,18 +24,27 @@ tools:
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
go get -t -u golang.org/x/tools/cmd/cover
|
||||
go install github.com/sonatype-nexus-community/nancy@latest
|
||||
go install golang.org/x/perf/cmd/benchstat@latest
|
||||
go install github.com/cespare/prettybench@latest
|
||||
go mod tidy
|
||||
|
||||
# brew install hougesen/tap/mdsf
|
||||
|
||||
lint:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 ./...
|
||||
# mdsf verify --debug --log-level warn docs/
|
||||
lint-fix:
|
||||
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
|
||||
# mdsf format --debug --log-level warn docs/
|
||||
|
||||
audit: tools
|
||||
audit:
|
||||
go list -json -m all | nancy sleuth
|
||||
|
||||
outdated: tools
|
||||
outdated:
|
||||
go list -u -m -json all | go-mod-outdated -update -direct
|
||||
|
||||
weight: tools
|
||||
weight:
|
||||
goweight
|
||||
|
||||
doc:
|
||||
cd docs && npm install && npm start
|
||||
|
||||
540
vendor/github.com/samber/lo/README.md
generated
vendored
540
vendor/github.com/samber/lo/README.md
generated
vendored
File diff suppressed because it is too large
Load Diff
54
vendor/github.com/samber/lo/channel.go
generated
vendored
54
vendor/github.com/samber/lo/channel.go
generated
vendored
@@ -5,15 +5,17 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo/internal/rand"
|
||||
"github.com/samber/lo/internal/xrand"
|
||||
)
|
||||
|
||||
// DispatchingStrategy is a function that distributes messages to channels.
|
||||
type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) int
|
||||
|
||||
// ChannelDispatcher distributes messages from input channels into N child channels.
|
||||
// Close events are propagated to children.
|
||||
// Underlying channels can have a fixed buffer capacity or be unbuffered when cap is 0.
|
||||
func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T {
|
||||
// Play: https://go.dev/play/p/UZGu2wVg3J2
|
||||
func ChannelDispatcher[T any](stream <-chan T, count, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T {
|
||||
children := createChannels[T](count, channelBufferCap)
|
||||
|
||||
roChildren := channelsToReadOnly(children)
|
||||
@@ -22,7 +24,7 @@ func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int,
|
||||
// propagate channel closing to children
|
||||
defer closeChannels(children)
|
||||
|
||||
var i uint64 = 0
|
||||
var i uint64
|
||||
|
||||
for {
|
||||
msg, ok := <-stream
|
||||
@@ -40,7 +42,7 @@ func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int,
|
||||
return roChildren
|
||||
}
|
||||
|
||||
func createChannels[T any](count int, channelBufferCap int) []chan T {
|
||||
func createChannels[T any](count, channelBufferCap int) []chan T {
|
||||
children := make([]chan T, 0, count)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
@@ -72,6 +74,7 @@ func channelIsNotFull[T any](ch <-chan T) bool {
|
||||
|
||||
// DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner.
|
||||
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||
// Play: https://go.dev/play/p/UZGu2wVg3J2
|
||||
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int {
|
||||
for {
|
||||
i := int(index % uint64(len(channels)))
|
||||
@@ -86,9 +89,10 @@ func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan
|
||||
|
||||
// DispatchingStrategyRandom distributes messages in a random manner.
|
||||
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||
// Play: https://go.dev/play/p/GEyGn3TdGk4
|
||||
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int {
|
||||
for {
|
||||
i := rand.IntN(len(channels))
|
||||
i := xrand.IntN(len(channels))
|
||||
if channelIsNotFull(channels[i]) {
|
||||
return i
|
||||
}
|
||||
@@ -99,6 +103,7 @@ func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T)
|
||||
|
||||
// DispatchingStrategyWeightedRandom distributes messages in a weighted manner.
|
||||
// If the channel capacity is exceeded, another random channel will be selected and so on.
|
||||
// Play: https://go.dev/play/p/v0eMh8NZG2L
|
||||
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
|
||||
seq := []int{}
|
||||
|
||||
@@ -110,7 +115,7 @@ func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy
|
||||
|
||||
return func(msg T, index uint64, channels []<-chan T) int {
|
||||
for {
|
||||
i := seq[rand.IntN(len(seq))]
|
||||
i := seq[xrand.IntN(len(seq))]
|
||||
if channelIsNotFull(channels[i]) {
|
||||
return i
|
||||
}
|
||||
@@ -122,6 +127,7 @@ func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy
|
||||
|
||||
// DispatchingStrategyFirst distributes messages in the first non-full channel.
|
||||
// If the capacity of the first channel is exceeded, the second channel will be selected and so on.
|
||||
// Play: https://go.dev/play/p/OrJCvOmk42f
|
||||
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int {
|
||||
for {
|
||||
for i := range channels {
|
||||
@@ -135,25 +141,28 @@ func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) i
|
||||
}
|
||||
|
||||
// DispatchingStrategyLeast distributes messages in the emptiest channel.
|
||||
// Play: https://go.dev/play/p/ypy0jrRcEe7
|
||||
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int {
|
||||
seq := Range(len(channels))
|
||||
|
||||
return MinBy(seq, func(item int, min int) bool {
|
||||
return len(channels[item]) < len(channels[min])
|
||||
return MinBy(seq, func(item, mIn int) bool {
|
||||
return len(channels[item]) < len(channels[mIn])
|
||||
})
|
||||
}
|
||||
|
||||
// DispatchingStrategyMost distributes messages in the fullest channel.
|
||||
// If the channel capacity is exceeded, the next channel will be selected and so on.
|
||||
// Play: https://go.dev/play/p/erHHone7rF9
|
||||
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int {
|
||||
seq := Range(len(channels))
|
||||
|
||||
return MaxBy(seq, func(item int, max int) bool {
|
||||
return len(channels[item]) > len(channels[max]) && channelIsNotFull(channels[item])
|
||||
return MaxBy(seq, func(item, mAx int) bool {
|
||||
return len(channels[item]) > len(channels[mAx]) && channelIsNotFull(channels[item])
|
||||
})
|
||||
}
|
||||
|
||||
// SliceToChannel returns a read-only channels of collection elements.
|
||||
// SliceToChannel returns a read-only channel of collection elements.
|
||||
// Play: https://go.dev/play/p/lIbSY3QmiEg
|
||||
func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
|
||||
ch := make(chan T, bufferSize)
|
||||
|
||||
@@ -168,7 +177,8 @@ func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
|
||||
return ch
|
||||
}
|
||||
|
||||
// ChannelToSlice returns a slice built from channels items. Blocks until channel closes.
|
||||
// ChannelToSlice returns a slice built from channel items. Blocks until channel closes.
|
||||
// Play: https://go.dev/play/p/lIbSY3QmiEg
|
||||
func ChannelToSlice[T any](ch <-chan T) []T {
|
||||
collection := []T{}
|
||||
|
||||
@@ -180,6 +190,9 @@ func ChannelToSlice[T any](ch <-chan T) []T {
|
||||
}
|
||||
|
||||
// Generator implements the generator design pattern.
|
||||
// Play: https://go.dev/play/p/lIbSY3QmiEg
|
||||
//
|
||||
// Deprecated: use "iter" package instead (Go >= 1.23).
|
||||
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
|
||||
ch := make(chan T, bufferSize)
|
||||
|
||||
@@ -196,7 +209,8 @@ func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
|
||||
}
|
||||
|
||||
// Buffer creates a slice of n elements from a channel. Returns the slice and the slice length.
|
||||
// @TODO: we should probably provide an helper that reuse the same buffer.
|
||||
// @TODO: we should probably provide a helper that reuses the same buffer.
|
||||
// Play: https://go.dev/play/p/gPQ-6xmcKQI
|
||||
func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||
buffer := make([]T, 0, size)
|
||||
index := 0
|
||||
@@ -222,7 +236,8 @@ func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime t
|
||||
}
|
||||
|
||||
// BufferWithContext creates a slice of n elements from a channel, with context. Returns the slice and the slice length.
|
||||
// @TODO: we should probably provide an helper that reuse the same buffer.
|
||||
// @TODO: we should probably provide a helper that reuses the same buffer.
|
||||
// Play: https://go.dev/play/p/oRfOyJWK9YF
|
||||
func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||
buffer := make([]T, 0, size)
|
||||
now := time.Now()
|
||||
@@ -245,6 +260,7 @@ func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (colle
|
||||
}
|
||||
|
||||
// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
|
||||
// Play: https://go.dev/play/p/sxyEM3koo4n
|
||||
func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
@@ -259,7 +275,8 @@ func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (coll
|
||||
}
|
||||
|
||||
// FanIn collects messages from multiple input channels into a single buffered channel.
|
||||
// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes.
|
||||
// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes.
|
||||
// Play: https://go.dev/play/p/FH8Wq-T04Jb
|
||||
func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||
out := make(chan T, channelBufferCap)
|
||||
var wg sync.WaitGroup
|
||||
@@ -284,7 +301,7 @@ func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||
}
|
||||
|
||||
// ChannelMerge collects messages from multiple input channels into a single buffered channel.
|
||||
// Output messages has no priority. When all upstream channels reach EOF, downstream channel closes.
|
||||
// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes.
|
||||
//
|
||||
// Deprecated: Use [FanIn] instead.
|
||||
func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||
@@ -292,9 +309,10 @@ func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
|
||||
}
|
||||
|
||||
// FanOut broadcasts all the upstream messages to multiple downstream channels.
|
||||
// When upstream channel reach EOF, downstream channels close. If any downstream
|
||||
// When upstream channel reaches EOF, downstream channels close. If any downstream
|
||||
// channels is full, broadcasting is paused.
|
||||
func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T {
|
||||
// Play: https://go.dev/play/p/2LHxcjKX23L
|
||||
func FanOut[T any](count, channelsBufferCap int, upstream <-chan T) []<-chan T {
|
||||
downstreams := createChannels[T](count, channelsBufferCap)
|
||||
|
||||
go func() {
|
||||
|
||||
19
vendor/github.com/samber/lo/concurrency.go
generated
vendored
19
vendor/github.com/samber/lo/concurrency.go
generated
vendored
@@ -17,9 +17,10 @@ func (s *synchronize) Do(cb func()) {
|
||||
}
|
||||
|
||||
// Synchronize wraps the underlying callback in a mutex. It receives an optional mutex.
|
||||
func Synchronize(opt ...sync.Locker) *synchronize {
|
||||
// Play: https://go.dev/play/p/X3cqROSpQmu
|
||||
func Synchronize(opt ...sync.Locker) *synchronize { //nolint:revive
|
||||
if len(opt) > 1 {
|
||||
panic("unexpected arguments")
|
||||
panic("lo.Synchronize: unexpected arguments")
|
||||
} else if len(opt) == 0 {
|
||||
opt = append(opt, &sync.Mutex{})
|
||||
}
|
||||
@@ -30,6 +31,7 @@ func Synchronize(opt ...sync.Locker) *synchronize {
|
||||
}
|
||||
|
||||
// Async executes a function in a goroutine and returns the result in a channel.
|
||||
// Play: https://go.dev/play/p/uo35gosuTLw
|
||||
func Async[A any](f func() A) <-chan A {
|
||||
ch := make(chan A, 1)
|
||||
go func() {
|
||||
@@ -39,6 +41,7 @@ func Async[A any](f func() A) <-chan A {
|
||||
}
|
||||
|
||||
// Async0 executes a function in a goroutine and returns a channel set once the function finishes.
|
||||
// Play: https://go.dev/play/p/tNqf1cClG_o
|
||||
func Async0(f func()) <-chan struct{} {
|
||||
ch := make(chan struct{}, 1)
|
||||
go func() {
|
||||
@@ -49,11 +52,13 @@ func Async0(f func()) <-chan struct{} {
|
||||
}
|
||||
|
||||
// Async1 is an alias to Async.
|
||||
// Play: https://go.dev/play/p/uo35gosuTLw
|
||||
func Async1[A any](f func() A) <-chan A {
|
||||
return Async(f)
|
||||
}
|
||||
|
||||
// Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel.
|
||||
// Play: https://go.dev/play/p/7W7mKQi0AhA
|
||||
func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
|
||||
ch := make(chan Tuple2[A, B], 1)
|
||||
go func() {
|
||||
@@ -63,6 +68,7 @@ func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
|
||||
}
|
||||
|
||||
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
|
||||
// Play: https://go.dev/play/p/L1d6o6l6q0d
|
||||
func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
|
||||
ch := make(chan Tuple3[A, B, C], 1)
|
||||
go func() {
|
||||
@@ -72,6 +78,7 @@ func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
|
||||
}
|
||||
|
||||
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
|
||||
// Play: https://go.dev/play/p/1X7q6oL0TqF
|
||||
func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
|
||||
ch := make(chan Tuple4[A, B, C, D], 1)
|
||||
go func() {
|
||||
@@ -81,6 +88,7 @@ func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
|
||||
}
|
||||
|
||||
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
|
||||
// Play: https://go.dev/play/p/2W7q4oL1TqG
|
||||
func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C, D, E] {
|
||||
ch := make(chan Tuple5[A, B, C, D, E], 1)
|
||||
go func() {
|
||||
@@ -90,6 +98,7 @@ func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C,
|
||||
}
|
||||
|
||||
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
|
||||
// Play: https://go.dev/play/p/3X8q5pM2UrH
|
||||
func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, B, C, D, E, F] {
|
||||
ch := make(chan Tuple6[A, B, C, D, E, F], 1)
|
||||
go func() {
|
||||
@@ -99,7 +108,8 @@ func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A,
|
||||
}
|
||||
|
||||
// WaitFor runs periodically until a condition is validated.
|
||||
func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
|
||||
// Play: https://go.dev/play/p/t_wTDmubbK3
|
||||
func WaitFor(condition func(i int) bool, timeout, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
|
||||
conditionWithContext := func(_ context.Context, currentIteration int) bool {
|
||||
return condition(currentIteration)
|
||||
}
|
||||
@@ -107,7 +117,8 @@ func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay t
|
||||
}
|
||||
|
||||
// WaitForWithContext runs periodically until a condition is validated or context is canceled.
|
||||
func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
|
||||
// Play: https://go.dev/play/p/t_wTDmubbK3
|
||||
func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
|
||||
start := time.Now()
|
||||
|
||||
if ctx.Err() != nil {
|
||||
|
||||
18
vendor/github.com/samber/lo/condition.go
generated
vendored
18
vendor/github.com/samber/lo/condition.go
generated
vendored
@@ -1,9 +1,9 @@
|
||||
package lo
|
||||
|
||||
// Ternary is a 1 line if/else statement.
|
||||
// Ternary is a single line if/else statement.
|
||||
// Take care to avoid dereferencing potentially nil pointers in your A/B expressions, because they are both evaluated. See TernaryF to avoid this problem.
|
||||
// Play: https://go.dev/play/p/t-D7WBL44h2
|
||||
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
||||
func Ternary[T any](condition bool, ifOutput, elseOutput T) T {
|
||||
if condition {
|
||||
return ifOutput
|
||||
}
|
||||
@@ -11,9 +11,9 @@ func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
|
||||
return elseOutput
|
||||
}
|
||||
|
||||
// TernaryF is a 1 line if/else statement whose options are functions
|
||||
// TernaryF is a single line if/else statement whose options are functions.
|
||||
// Play: https://go.dev/play/p/AO4VW20JoqM
|
||||
func TernaryF[T any](condition bool, ifFunc func() T, elseFunc func() T) T {
|
||||
func TernaryF[T any](condition bool, ifFunc, elseFunc func() T) T {
|
||||
if condition {
|
||||
return ifFunc()
|
||||
}
|
||||
@@ -26,9 +26,9 @@ type ifElse[T any] struct {
|
||||
done bool
|
||||
}
|
||||
|
||||
// If.
|
||||
// If is a single line if/else statement.
|
||||
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||
func If[T any](condition bool, result T) *ifElse[T] {
|
||||
func If[T any](condition bool, result T) *ifElse[T] { //nolint:revive
|
||||
if condition {
|
||||
return &ifElse[T]{result, true}
|
||||
}
|
||||
@@ -37,9 +37,9 @@ func If[T any](condition bool, result T) *ifElse[T] {
|
||||
return &ifElse[T]{t, false}
|
||||
}
|
||||
|
||||
// IfF.
|
||||
// IfF is a single line if/else statement whose options are functions.
|
||||
// Play: https://go.dev/play/p/WSw3ApMxhyW
|
||||
func IfF[T any](condition bool, resultF func() T) *ifElse[T] {
|
||||
func IfF[T any](condition bool, resultF func() T) *ifElse[T] { //nolint:revive
|
||||
if condition {
|
||||
return &ifElse[T]{resultF(), true}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ type switchCase[T comparable, R any] struct {
|
||||
|
||||
// Switch is a pure functional switch/case/default statement.
|
||||
// Play: https://go.dev/play/p/TGbKUMAeRUd
|
||||
func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
|
||||
func Switch[T comparable, R any](predicate T) *switchCase[T, R] { //nolint:revive
|
||||
var result R
|
||||
|
||||
return &switchCase[T, R]{
|
||||
|
||||
13
vendor/github.com/samber/lo/errors.go
generated
vendored
13
vendor/github.com/samber/lo/errors.go
generated
vendored
@@ -25,7 +25,7 @@ func messageFromMsgAndArgs(msgAndArgs ...any) string {
|
||||
return fmt.Sprintf("%+v", msgAndArgs[0])
|
||||
}
|
||||
if len(msgAndArgs) > 1 {
|
||||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
||||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) //nolint:errcheck,forcetypeassert
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -51,9 +51,8 @@ func must(err any, messageArgs ...any) {
|
||||
message := messageFromMsgAndArgs(messageArgs...)
|
||||
if message != "" {
|
||||
panic(message + ": " + e.Error())
|
||||
} else {
|
||||
panic(e.Error())
|
||||
}
|
||||
panic(e.Error())
|
||||
|
||||
default:
|
||||
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error")
|
||||
@@ -62,7 +61,7 @@ func must(err any, messageArgs ...any) {
|
||||
|
||||
// Must is a helper that wraps a call to a function returning a value and an error
|
||||
// and panics if err is error or false.
|
||||
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||
// Play: https://go.dev/play/p/fOqtX5HudtN
|
||||
func Must[T any](val T, err any, messageArgs ...any) T {
|
||||
must(err, messageArgs...)
|
||||
return val
|
||||
@@ -74,7 +73,7 @@ func Must0(err any, messageArgs ...any) {
|
||||
must(err, messageArgs...)
|
||||
}
|
||||
|
||||
// Must1 is an alias to Must
|
||||
// Must1 is an alias to Must.
|
||||
// Play: https://go.dev/play/p/TMoWrRp3DyC
|
||||
func Must1[T any](val T, err any, messageArgs ...any) T {
|
||||
return Must(val, err, messageArgs...)
|
||||
@@ -130,7 +129,7 @@ func Try(callback func() error) (ok bool) {
|
||||
ok = false
|
||||
}
|
||||
|
||||
return
|
||||
return ok
|
||||
}
|
||||
|
||||
// Try0 has the same behavior as Try, but callback returns no variable.
|
||||
@@ -328,7 +327,7 @@ func TryWithErrorValue(callback func() error) (errorValue any, ok bool) {
|
||||
errorValue = err
|
||||
}
|
||||
|
||||
return
|
||||
return errorValue, ok
|
||||
}
|
||||
|
||||
// TryCatch has the same behavior as Try, but calls the catch function in case of error.
|
||||
|
||||
243
vendor/github.com/samber/lo/find.go
generated
vendored
243
vendor/github.com/samber/lo/find.go
generated
vendored
@@ -5,11 +5,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo/internal/constraints"
|
||||
"github.com/samber/lo/internal/rand"
|
||||
"github.com/samber/lo/internal/xrand"
|
||||
)
|
||||
|
||||
// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1
|
||||
// IndexOf returns the index at which the first occurrence of a value is found in a slice or -1
|
||||
// if the value cannot be found.
|
||||
// Play: https://go.dev/play/p/Eo7W0lvKTky
|
||||
func IndexOf[T comparable](collection []T, element T) int {
|
||||
for i := range collection {
|
||||
if collection[i] == element {
|
||||
@@ -20,8 +21,9 @@ func IndexOf[T comparable](collection []T, element T) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// LastIndexOf returns the index at which the last occurrence of a value is found in an array or return -1
|
||||
// LastIndexOf returns the index at which the last occurrence of a value is found in a slice or -1
|
||||
// if the value cannot be found.
|
||||
// Play: https://go.dev/play/p/Eo7W0lvKTky
|
||||
func LastIndexOf[T comparable](collection []T, element T) int {
|
||||
length := len(collection)
|
||||
|
||||
@@ -34,7 +36,40 @@ func LastIndexOf[T comparable](collection []T, element T) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Find search an element in a slice based on a predicate. It returns element and true if element was found.
|
||||
// HasPrefix returns true if the collection has the prefix.
|
||||
// Play: https://go.dev/play/p/SrljzVDpMQM
|
||||
func HasPrefix[T comparable](collection, prefix []T) bool {
|
||||
if len(collection) < len(prefix) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range prefix {
|
||||
if collection[i] != prefix[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// HasSuffix returns true if the collection has the suffix.
|
||||
// Play: https://go.dev/play/p/bJeLetQNAON
|
||||
func HasSuffix[T comparable](collection, suffix []T) bool {
|
||||
if len(collection) < len(suffix) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range suffix {
|
||||
if collection[len(collection)-len(suffix)+i] != suffix[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Find searches for an element in a slice based on a predicate. Returns element and true if element was found.
|
||||
// Play: https://go.dev/play/p/Eo7W0lvKTky
|
||||
func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
|
||||
for i := range collection {
|
||||
if predicate(collection[i]) {
|
||||
@@ -46,8 +81,9 @@ func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
|
||||
return result, false
|
||||
}
|
||||
|
||||
// FindIndexOf searches an element in a slice based on a predicate and returns the index and true.
|
||||
// It returns -1 and false if the element is not found.
|
||||
// FindIndexOf searches for an element in a slice based on a predicate and returns the index and true.
|
||||
// Returns -1 and false if the element is not found.
|
||||
// Play: https://go.dev/play/p/XWSEM4Ic_t0
|
||||
func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
|
||||
for i := range collection {
|
||||
if predicate(collection[i]) {
|
||||
@@ -59,8 +95,9 @@ func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bo
|
||||
return result, -1, false
|
||||
}
|
||||
|
||||
// FindLastIndexOf searches last element in a slice based on a predicate and returns the index and true.
|
||||
// It returns -1 and false if the element is not found.
|
||||
// FindLastIndexOf searches for the last element in a slice based on a predicate and returns the index and true.
|
||||
// Returns -1 and false if the element is not found.
|
||||
// Play: https://go.dev/play/p/dPiMRtJ6cUx
|
||||
func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
|
||||
length := len(collection)
|
||||
|
||||
@@ -74,7 +111,8 @@ func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int
|
||||
return result, -1, false
|
||||
}
|
||||
|
||||
// FindOrElse search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.
|
||||
// FindOrElse searches for an element in a slice based on a predicate. Returns the element if found or a given fallback value otherwise.
|
||||
// Play: https://go.dev/play/p/Eo7W0lvKTky
|
||||
func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) T {
|
||||
for i := range collection {
|
||||
if predicate(collection[i]) {
|
||||
@@ -86,9 +124,10 @@ func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool)
|
||||
}
|
||||
|
||||
// FindKey returns the key of the first value matching.
|
||||
func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
|
||||
for k := range object {
|
||||
if object[k] == value {
|
||||
// Play: https://go.dev/play/p/Bg0w1VDPYXx
|
||||
func FindKey[K, V comparable](object map[K]V, value V) (K, bool) {
|
||||
for k, v := range object {
|
||||
if v == value {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
@@ -96,10 +135,11 @@ func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
|
||||
return Empty[K](), false
|
||||
}
|
||||
|
||||
// FindKeyBy returns the key of the first element predicate returns truthy for.
|
||||
// FindKeyBy returns the key of the first element predicate returns true for.
|
||||
// Play: https://go.dev/play/p/9IbiPElcyo8
|
||||
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool) {
|
||||
for k := range object {
|
||||
if predicate(k, object[k]) {
|
||||
for k, v := range object {
|
||||
if predicate(k, v) {
|
||||
return k, true
|
||||
}
|
||||
}
|
||||
@@ -107,7 +147,7 @@ func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value
|
||||
return Empty[K](), false
|
||||
}
|
||||
|
||||
// FindUniques returns a slice with all the unique elements of the collection.
|
||||
// FindUniques returns a slice with all the elements that appear in the collection only once.
|
||||
// The order of result values is determined by the order they occur in the collection.
|
||||
func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
|
||||
isDupl := make(map[T]bool, len(collection))
|
||||
@@ -132,9 +172,9 @@ func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
|
||||
return result
|
||||
}
|
||||
|
||||
// FindUniquesBy returns a slice with all the unique elements of the collection.
|
||||
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
|
||||
// invoked for each element in array to generate the criterion by which uniqueness is computed.
|
||||
// FindUniquesBy returns a slice with all the elements that appear in the collection only once.
|
||||
// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is
|
||||
// invoked for each element in the slice to generate the criterion by which uniqueness is computed.
|
||||
func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
|
||||
isDupl := make(map[U]bool, len(collection))
|
||||
|
||||
@@ -162,7 +202,7 @@ func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee f
|
||||
return result
|
||||
}
|
||||
|
||||
// FindDuplicates returns a slice with the first occurrence of each duplicated elements of the collection.
|
||||
// FindDuplicates returns a slice with the first occurrence of each duplicated element in the collection.
|
||||
// The order of result values is determined by the order they occur in the collection.
|
||||
func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
|
||||
isDupl := make(map[T]bool, len(collection))
|
||||
@@ -188,9 +228,9 @@ func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
|
||||
return result
|
||||
}
|
||||
|
||||
// FindDuplicatesBy returns a slice with the first occurrence of each duplicated elements of the collection.
|
||||
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
|
||||
// invoked for each element in array to generate the criterion by which uniqueness is computed.
|
||||
// FindDuplicatesBy returns a slice with the first occurrence of each duplicated element in the collection.
|
||||
// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is
|
||||
// invoked for each element in the slice to generate the criterion by which uniqueness is computed.
|
||||
func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
|
||||
isDupl := make(map[U]bool, len(collection))
|
||||
|
||||
@@ -221,122 +261,123 @@ func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iterate
|
||||
|
||||
// Min search the minimum value of a collection.
|
||||
// Returns zero value when the collection is empty.
|
||||
// Play: https://go.dev/play/p/r6e-Z8JozS8
|
||||
func Min[T constraints.Ordered](collection []T) T {
|
||||
var min T
|
||||
var mIn T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
mIn = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item < min {
|
||||
min = item
|
||||
if item < mIn {
|
||||
mIn = item
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
// MinIndex search the minimum value of a collection and the index of the minimum value.
|
||||
// Returns (zero value, -1) when the collection is empty.
|
||||
func MinIndex[T constraints.Ordered](collection []T) (T, int) {
|
||||
var (
|
||||
min T
|
||||
mIn T
|
||||
index int
|
||||
)
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min, -1
|
||||
return mIn, -1
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
mIn = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item < min {
|
||||
min = item
|
||||
if item < mIn {
|
||||
mIn = item
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return min, index
|
||||
return mIn, index
|
||||
}
|
||||
|
||||
// MinBy search the minimum value of a collection using the given comparison function.
|
||||
// If several values of the collection are equal to the smallest value, returns the first such value.
|
||||
// Returns zero value when the collection is empty.
|
||||
func MinBy[T any](collection []T, comparison func(a T, b T) bool) T {
|
||||
var min T
|
||||
func MinBy[T any](collection []T, comparison func(a, b T) bool) T {
|
||||
var mIn T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
mIn = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, min) {
|
||||
min = item
|
||||
if comparison(item, mIn) {
|
||||
mIn = item
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
// MinIndexBy search the minimum value of a collection using the given comparison function and the index of the minimum value.
|
||||
// If several values of the collection are equal to the smallest value, returns the first such value.
|
||||
// Returns (zero value, -1) when the collection is empty.
|
||||
func MinIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) {
|
||||
func MinIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
|
||||
var (
|
||||
min T
|
||||
mIn T
|
||||
index int
|
||||
)
|
||||
|
||||
if len(collection) == 0 {
|
||||
return min, -1
|
||||
return mIn, -1
|
||||
}
|
||||
|
||||
min = collection[0]
|
||||
mIn = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, min) {
|
||||
min = item
|
||||
if comparison(item, mIn) {
|
||||
mIn = item
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return min, index
|
||||
return mIn, index
|
||||
}
|
||||
|
||||
// Earliest search the minimum time.Time of a collection.
|
||||
// Returns zero value when the collection is empty.
|
||||
func Earliest(times ...time.Time) time.Time {
|
||||
var min time.Time
|
||||
var mIn time.Time
|
||||
|
||||
if len(times) == 0 {
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
min = times[0]
|
||||
mIn = times[0]
|
||||
|
||||
for i := 1; i < len(times); i++ {
|
||||
item := times[i]
|
||||
|
||||
if item.Before(min) {
|
||||
min = item
|
||||
if item.Before(mIn) {
|
||||
mIn = item
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
return mIn
|
||||
}
|
||||
|
||||
// EarliestBy search the minimum time.Time of a collection using the given iteratee function.
|
||||
@@ -365,122 +406,123 @@ func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T {
|
||||
|
||||
// Max searches the maximum value of a collection.
|
||||
// Returns zero value when the collection is empty.
|
||||
// Play: https://go.dev/play/p/r6e-Z8JozS8
|
||||
func Max[T constraints.Ordered](collection []T) T {
|
||||
var max T
|
||||
var mAx T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
mAx = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item > max {
|
||||
max = item
|
||||
if item > mAx {
|
||||
mAx = item
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
// MaxIndex searches the maximum value of a collection and the index of the maximum value.
|
||||
// Returns (zero value, -1) when the collection is empty.
|
||||
func MaxIndex[T constraints.Ordered](collection []T) (T, int) {
|
||||
var (
|
||||
max T
|
||||
mAx T
|
||||
index int
|
||||
)
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max, -1
|
||||
return mAx, -1
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
mAx = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if item > max {
|
||||
max = item
|
||||
if item > mAx {
|
||||
mAx = item
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return max, index
|
||||
return mAx, index
|
||||
}
|
||||
|
||||
// MaxBy search the maximum value of a collection using the given comparison function.
|
||||
// If several values of the collection are equal to the greatest value, returns the first such value.
|
||||
// Returns zero value when the collection is empty.
|
||||
func MaxBy[T any](collection []T, comparison func(a T, b T) bool) T {
|
||||
var max T
|
||||
func MaxBy[T any](collection []T, comparison func(a, b T) bool) T {
|
||||
var mAx T
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
mAx = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, max) {
|
||||
max = item
|
||||
if comparison(item, mAx) {
|
||||
mAx = item
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
// MaxIndexBy search the maximum value of a collection using the given comparison function and the index of the maximum value.
|
||||
// If several values of the collection are equal to the greatest value, returns the first such value.
|
||||
// Returns (zero value, -1) when the collection is empty.
|
||||
func MaxIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int) {
|
||||
func MaxIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
|
||||
var (
|
||||
max T
|
||||
mAx T
|
||||
index int
|
||||
)
|
||||
|
||||
if len(collection) == 0 {
|
||||
return max, -1
|
||||
return mAx, -1
|
||||
}
|
||||
|
||||
max = collection[0]
|
||||
mAx = collection[0]
|
||||
|
||||
for i := 1; i < len(collection); i++ {
|
||||
item := collection[i]
|
||||
|
||||
if comparison(item, max) {
|
||||
max = item
|
||||
if comparison(item, mAx) {
|
||||
mAx = item
|
||||
index = i
|
||||
}
|
||||
}
|
||||
|
||||
return max, index
|
||||
return mAx, index
|
||||
}
|
||||
|
||||
// Latest search the maximum time.Time of a collection.
|
||||
// Returns zero value when the collection is empty.
|
||||
func Latest(times ...time.Time) time.Time {
|
||||
var max time.Time
|
||||
var mAx time.Time
|
||||
|
||||
if len(times) == 0 {
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
max = times[0]
|
||||
mAx = times[0]
|
||||
|
||||
for i := 1; i < len(times); i++ {
|
||||
item := times[i]
|
||||
|
||||
if item.After(max) {
|
||||
max = item
|
||||
if item.After(mAx) {
|
||||
mAx = item
|
||||
}
|
||||
}
|
||||
|
||||
return max
|
||||
return mAx
|
||||
}
|
||||
|
||||
// LatestBy search the maximum time.Time of a collection using the given iteratee function.
|
||||
@@ -508,6 +550,7 @@ func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T {
|
||||
}
|
||||
|
||||
// First returns the first element of a collection and check for availability of the first element.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func First[T any](collection []T) (T, bool) {
|
||||
length := len(collection)
|
||||
|
||||
@@ -520,12 +563,14 @@ func First[T any](collection []T) (T, bool) {
|
||||
}
|
||||
|
||||
// FirstOrEmpty returns the first element of a collection or zero value if empty.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func FirstOrEmpty[T any](collection []T) T {
|
||||
i, _ := First(collection)
|
||||
return i
|
||||
}
|
||||
|
||||
// FirstOr returns the first element of a collection or the fallback value if empty.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func FirstOr[T any](collection []T, fallback T) T {
|
||||
i, ok := First(collection)
|
||||
if !ok {
|
||||
@@ -536,6 +581,7 @@ func FirstOr[T any](collection []T, fallback T) T {
|
||||
}
|
||||
|
||||
// Last returns the last element of a collection or error if empty.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func Last[T any](collection []T) (T, bool) {
|
||||
length := len(collection)
|
||||
|
||||
@@ -548,12 +594,14 @@ func Last[T any](collection []T) (T, bool) {
|
||||
}
|
||||
|
||||
// LastOrEmpty returns the last element of a collection or zero value if empty.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func LastOrEmpty[T any](collection []T) T {
|
||||
i, _ := Last(collection)
|
||||
return i
|
||||
}
|
||||
|
||||
// LastOr returns the last element of a collection or the fallback value if empty.
|
||||
// Play: https://go.dev/play/p/ul45Z0y2EFO
|
||||
func LastOr[T any](collection []T, fallback T) T {
|
||||
i, ok := Last(collection)
|
||||
if !ok {
|
||||
@@ -565,6 +613,7 @@ func LastOr[T any](collection []T, fallback T) T {
|
||||
|
||||
// Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element
|
||||
// from the end is returned. An error is returned when nth is out of slice bounds.
|
||||
// Play: https://go.dev/play/p/sHoh88KWt6B
|
||||
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
|
||||
n := int(nth)
|
||||
l := len(collection)
|
||||
@@ -582,6 +631,7 @@ func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
|
||||
// NthOr returns the element at index `nth` of collection.
|
||||
// If `nth` is negative, it returns the nth element from the end.
|
||||
// If `nth` is out of slice bounds, it returns the fallback value instead of an error.
|
||||
// Play: https://go.dev/play/p/sHoh88KWt6B
|
||||
func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
|
||||
value, err := Nth(collection, nth)
|
||||
if err != nil {
|
||||
@@ -593,6 +643,7 @@ func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
|
||||
// NthOrEmpty returns the element at index `nth` of collection.
|
||||
// If `nth` is negative, it returns the nth element from the end.
|
||||
// If `nth` is out of slice bounds, it returns the zero value (empty value) for that type.
|
||||
// Play: https://go.dev/play/p/sHoh88KWt6B
|
||||
func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T {
|
||||
value, err := Nth(collection, nth)
|
||||
if err != nil {
|
||||
@@ -603,16 +654,18 @@ func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T {
|
||||
}
|
||||
|
||||
// randomIntGenerator is a function that should return a random integer in the range [0, n)
|
||||
// where n is the parameter passed to the randomIntGenerator.
|
||||
// where n is the argument passed to the randomIntGenerator.
|
||||
type randomIntGenerator func(n int) int
|
||||
|
||||
// Sample returns a random item from collection.
|
||||
// Play: https://go.dev/play/p/vCcSJbh5s6l
|
||||
func Sample[T any](collection []T) T {
|
||||
result := SampleBy(collection, rand.IntN)
|
||||
result := SampleBy(collection, xrand.IntN)
|
||||
return result
|
||||
}
|
||||
|
||||
// SampleBy returns a random item from collection, using randomIntGenerator as the random index generator.
|
||||
// Play: https://go.dev/play/p/HDmKmMgq0XN
|
||||
func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
|
||||
size := len(collection)
|
||||
if size == 0 {
|
||||
@@ -622,16 +675,18 @@ func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
|
||||
}
|
||||
|
||||
// Samples returns N random unique items from collection.
|
||||
// Play: https://go.dev/play/p/vCcSJbh5s6l
|
||||
func Samples[T any, Slice ~[]T](collection Slice, count int) Slice {
|
||||
results := SamplesBy(collection, count, rand.IntN)
|
||||
results := SamplesBy(collection, count, xrand.IntN)
|
||||
return results
|
||||
}
|
||||
|
||||
// SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator.
|
||||
// Play: https://go.dev/play/p/HDmKmMgq0XN
|
||||
func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator randomIntGenerator) Slice {
|
||||
size := len(collection)
|
||||
|
||||
copy := append(Slice{}, collection...)
|
||||
cOpy := append(Slice{}, collection...)
|
||||
|
||||
results := Slice{}
|
||||
|
||||
@@ -639,12 +694,12 @@ func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerato
|
||||
copyLength := size - i
|
||||
|
||||
index := randomIntGenerator(size - i)
|
||||
results = append(results, copy[index])
|
||||
results = append(results, cOpy[index])
|
||||
|
||||
// Removes element.
|
||||
// It is faster to swap with last element and remove it.
|
||||
copy[index] = copy[copyLength-1]
|
||||
copy = copy[:copyLength-1]
|
||||
cOpy[index] = cOpy[copyLength-1]
|
||||
cOpy = cOpy[:copyLength-1]
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
8
vendor/github.com/samber/lo/func.go
generated
vendored
8
vendor/github.com/samber/lo/func.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package lo
|
||||
|
||||
// Partial returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/Sy1gAQiQZ3v
|
||||
func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R {
|
||||
return func(t2 T2) R {
|
||||
return f(arg1, t2)
|
||||
@@ -8,11 +9,13 @@ func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R {
|
||||
}
|
||||
|
||||
// Partial1 returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/D-ASTXCLBzw
|
||||
func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R {
|
||||
return Partial(f, arg1)
|
||||
}
|
||||
|
||||
// Partial2 returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/-xiPjy4JChJ
|
||||
func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R {
|
||||
return func(t2 T2, t3 T3) R {
|
||||
return f(arg1, t2, t3)
|
||||
@@ -20,6 +23,7 @@ func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R {
|
||||
}
|
||||
|
||||
// Partial3 returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/zWtSutpI26m
|
||||
func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, T3, T4) R {
|
||||
return func(t2 T2, t3 T3, t4 T4) R {
|
||||
return f(arg1, t2, t3, t4)
|
||||
@@ -27,13 +31,15 @@ func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2,
|
||||
}
|
||||
|
||||
// Partial4 returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/kBrnnMTcJm0
|
||||
func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1) func(T2, T3, T4, T5) R {
|
||||
return func(t2 T2, t3 T3, t4 T4, t5 T5) R {
|
||||
return f(arg1, t2, t3, t4, t5)
|
||||
}
|
||||
}
|
||||
|
||||
// Partial5 returns new function that, when called, has its first argument set to the provided value
|
||||
// Partial5 returns new function that, when called, has its first argument set to the provided value.
|
||||
// Play: https://go.dev/play/p/7Is7K2y_VC3
|
||||
func Partial5[T1, T2, T3, T4, T5, T6, R any](f func(T1, T2, T3, T4, T5, T6) R, arg1 T1) func(T2, T3, T4, T5, T6) R {
|
||||
return func(t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R {
|
||||
return f(arg1, t2, t3, t4, t5, t6)
|
||||
|
||||
4
vendor/github.com/samber/lo/internal/constraints/README.md
generated
vendored
Normal file
4
vendor/github.com/samber/lo/internal/constraints/README.md
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
# Constraints
|
||||
|
||||
This package is for Go 1.18 retrocompatiblity purpose.
|
||||
4
vendor/github.com/samber/lo/internal/constraints/ordered_go121.go
generated
vendored
4
vendor/github.com/samber/lo/internal/constraints/ordered_go121.go
generated
vendored
@@ -6,4 +6,8 @@ import (
|
||||
"cmp"
|
||||
)
|
||||
|
||||
// Ordered is a constraint that permits any ordered type: any type
|
||||
// that supports the operators < <= >= >.
|
||||
// If future releases of Go add new ordered types,
|
||||
// this constraint will be modified to include them.
|
||||
type Ordered = cmp.Ordered
|
||||
|
||||
17
vendor/github.com/samber/lo/internal/rand/ordered_go122.go
generated
vendored
17
vendor/github.com/samber/lo/internal/rand/ordered_go122.go
generated
vendored
@@ -1,17 +0,0 @@
|
||||
//go:build go1.22
|
||||
|
||||
package rand
|
||||
|
||||
import "math/rand/v2"
|
||||
|
||||
func Shuffle(n int, swap func(i, j int)) {
|
||||
rand.Shuffle(n, swap)
|
||||
}
|
||||
|
||||
func IntN(n int) int {
|
||||
return rand.IntN(n)
|
||||
}
|
||||
|
||||
func Int64() int64 {
|
||||
return rand.Int64()
|
||||
}
|
||||
@@ -1,22 +1,28 @@
|
||||
//go:build !go1.22
|
||||
|
||||
package rand
|
||||
package xrand
|
||||
|
||||
import "math/rand"
|
||||
|
||||
// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
||||
func Shuffle(n int, swap func(i, j int)) {
|
||||
rand.Shuffle(n, swap)
|
||||
}
|
||||
|
||||
// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n)
|
||||
// from the default Source.
|
||||
// It panics if n <= 0.
|
||||
func IntN(n int) int {
|
||||
// bearer:disable go_gosec_crypto_weak_random
|
||||
return rand.Intn(n)
|
||||
}
|
||||
|
||||
// Int64 returns a non-negative pseudo-random 63-bit integer as an int64
|
||||
// from the default Source.
|
||||
func Int64() int64 {
|
||||
// bearer:disable go_gosec_crypto_weak_random
|
||||
n := rand.Int63()
|
||||
|
||||
|
||||
// bearer:disable go_gosec_crypto_weak_random
|
||||
if rand.Intn(2) == 0 {
|
||||
return -n
|
||||
23
vendor/github.com/samber/lo/internal/xrand/ordered_go122.go
generated
vendored
Normal file
23
vendor/github.com/samber/lo/internal/xrand/ordered_go122.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
//go:build go1.22
|
||||
|
||||
package xrand
|
||||
|
||||
import "math/rand/v2"
|
||||
|
||||
// Shuffle returns a slice of shuffled values. Uses the Fisher-Yates shuffle algorithm.
|
||||
func Shuffle(n int, swap func(i, j int)) {
|
||||
rand.Shuffle(n, swap)
|
||||
}
|
||||
|
||||
// IntN returns, as an int, a pseudo-random number in the half-open interval [0,n)
|
||||
// from the default Source.
|
||||
// It panics if n <= 0.
|
||||
func IntN(n int) int {
|
||||
return rand.IntN(n)
|
||||
}
|
||||
|
||||
// Int64 returns a non-negative pseudo-random 63-bit integer as an int64
|
||||
// from the default Source.
|
||||
func Int64() int64 {
|
||||
return rand.Int64()
|
||||
}
|
||||
6
vendor/github.com/samber/lo/internal/xtime/README.md
generated
vendored
Normal file
6
vendor/github.com/samber/lo/internal/xtime/README.md
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
# xtime
|
||||
|
||||
Lightweight mock for time package.
|
||||
|
||||
A dedicated package such as [jonboulle/clockwork](https://github.com/jonboulle/clockwork/) would be better, but I would rather limit dependencies for this package. `clockwork` does not support Go 1.18 anymore.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user