fix cross folder copy

also update css/js deps and other minor changes

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2023-04-13 18:23:42 +02:00
parent 6279216c2e
commit 3cb53b2c33
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
25 changed files with 322 additions and 131 deletions

View file

@ -1,6 +1,6 @@
# Configuring SFTPGo
<details><summary><font size=5> Command line option</font></summary>
<details><summary><font size=5> Command line options</font></summary>
The SFTPGo executable can be used this way:

35
go.mod
View file

@ -10,14 +10,14 @@ require (
github.com/alexedwards/argon2id v0.0.0-20230305115115-4b3c3280a736
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964
github.com/aws/aws-sdk-go-v2 v1.17.8
github.com/aws/aws-sdk-go-v2/config v1.18.20
github.com/aws/aws-sdk-go-v2/credentials v1.13.19
github.com/aws/aws-sdk-go-v2/config v1.18.21
github.com/aws/aws-sdk-go-v2/credentials v1.13.20
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.61
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.8
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.2
github.com/aws/aws-sdk-go-v2/service/sts v1.18.8
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.62
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.9
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.3
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.3
github.com/aws/aws-sdk-go-v2/service/sts v1.18.9
github.com/bmatcuk/doublestar/v4 v4.6.0
github.com/cockroachdb/cockroach-go/v2 v2.3.3
github.com/coreos/go-oidc/v3 v3.5.0
@ -36,7 +36,7 @@ require (
github.com/hashicorp/go-hclog v1.5.0
github.com/hashicorp/go-plugin v1.4.10-0.20230403150917-e889c1ba1044
github.com/hashicorp/go-retryablehttp v0.7.2
github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf
github.com/jackc/pgx/v5 v5.3.2-0.20230411230705-2cf1541bb90a
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
github.com/klauspost/compress v1.16.4
github.com/lestrrat-go/jwx/v2 v2.0.9
@ -48,11 +48,11 @@ require (
github.com/pires/go-proxyproto v0.7.0
github.com/pkg/sftp v1.13.6-0.20230213180117-971c283182b6
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.14.0
github.com/prometheus/client_golang v1.15.0
github.com/robfig/cron/v3 v3.0.1
github.com/rs/cors v1.8.3
github.com/rs/xid v1.4.0
github.com/rs/zerolog v1.29.0
github.com/rs/cors v1.9.0
github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.29.1
github.com/sftpgo/sdk v0.1.3-0.20230406132142-15f26d806282
github.com/shirou/gopsutil/v3 v3.23.3
github.com/spf13/afero v1.9.5
@ -74,13 +74,13 @@ require (
golang.org/x/sys v0.7.0
golang.org/x/term v0.7.0
golang.org/x/time v0.3.0
google.golang.org/api v0.116.0
google.golang.org/api v0.118.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
)
require (
cloud.google.com/go v0.110.0 // indirect
cloud.google.com/go/compute v1.19.0 // indirect
cloud.google.com/go/compute v1.19.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
@ -94,8 +94,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.27 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.26 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.12.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8 // indirect
github.com/aws/smithy-go v1.13.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
@ -113,6 +113,7 @@ require (
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/s2a-go v0.1.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
github.com/googleapis/gax-go/v2 v2.8.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
@ -159,7 +160,7 @@ require (
golang.org/x/tools v0.8.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/grpc v1.54.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

71
go.sum
View file

@ -124,8 +124,8 @@ cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARy
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA=
cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs=
cloud.google.com/go/compute v1.19.0 h1:+9zda3WGgW1ZSTlVppLCYFIr48Pa35q1uG2N1itbCEQ=
cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU=
cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY=
cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE=
cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@ -230,7 +230,7 @@ cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxs
cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg=
cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0=
cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg=
cloud.google.com/go/kms v1.10.0 h1:Imrtp8792uqNP9bdfPrjtUkjjqOMBcAJ2bdFaAnLhnk=
cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g=
cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE=
@ -565,17 +565,17 @@ github.com/aws/aws-sdk-go-v2 v1.17.8/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3eP
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 h1:dK82zF6kkPeCo8J1e+tGx4JdvDIQzj7ygIoLg8WMuGs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10/go.mod h1:VeTZetY5KRJLuD/7fkQXMU6Mw7H5m/KP2J5Iy9osMno=
github.com/aws/aws-sdk-go-v2/config v1.18.12/go.mod h1:J36fOhj1LQBr+O4hJCiT8FwVvieeoSGOtPuvhKlsNu8=
github.com/aws/aws-sdk-go-v2/config v1.18.20 h1:yYy+onqmLmDVZtx0mkqbx8aJPl+58V6ivLbLDZ2Qztc=
github.com/aws/aws-sdk-go-v2/config v1.18.20/go.mod h1:RWjF39RiDevmHw/+VaD8F0A36OPIPTHQQyRx0eZohnw=
github.com/aws/aws-sdk-go-v2/config v1.18.21 h1:ENTXWKwE8b9YXgQCsruGLhvA9bhg+RqAsL9XEMEsa2c=
github.com/aws/aws-sdk-go-v2/config v1.18.21/go.mod h1:+jPQiVPz1diRnjj6VGqWcLK6EzNmQ42l7J3OqGTLsSY=
github.com/aws/aws-sdk-go-v2/credentials v1.13.12/go.mod h1:37HG2MBroXK3jXfxVGtbM2J48ra2+Ltu+tmwr/jO0KA=
github.com/aws/aws-sdk-go-v2/credentials v1.13.19 h1:FWHJy9uggyQCSEhovtl/6W6rW9P6DSr62GUeY/TS6Eo=
github.com/aws/aws-sdk-go-v2/credentials v1.13.19/go.mod h1:2m4uvLvl5hvQezVkLeBBUGMEDm5GcUNc3016W6d3NGg=
github.com/aws/aws-sdk-go-v2/credentials v1.13.20 h1:oZCEFcrMppP/CNiS8myzv9JgOzq2s0d3v3MXYil/mxQ=
github.com/aws/aws-sdk-go-v2/credentials v1.13.20/go.mod h1:xtZnXErtbZ8YGXC3+8WfajpMBn5Ga/3ojZdxHq6iI8o=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.22/go.mod h1:YGSIJyQ6D6FjKMQh16hVFSIUD54L4F7zTGePqYMYYJU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2 h1:jOzQAesnBFDmz93feqKnsTHsXrlwWORNZMFHMV+WLFU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.2/go.mod h1:cDh1p6XkSGSwSRIArWRc6+UqAQ7x4alQ0QfpVR6f+co=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.51/go.mod h1:7Grl2gV+dx9SWrUIgwwlUvU40t7+lOSbx34XwfmsTkY=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.61 h1:0fHTNkoMAz7jbXSyo0SLubbTJEO+AgvkZ0iWT9DbEsk=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.61/go.mod h1:i8l1At/vjpY8xf1ivKUBJE4+DQyE0gM9Zdg3ZpC/7RU=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.62 h1:LhVbe/UDWvBT/jp5LYAweFVH8s+DNtT07Qp2riWEovU=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.62/go.mod h1:4xCuu1TSwhW5UH6WOdtS4/x/9UfMr2XplzKc86Ffj78=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.28/go.mod h1:3lwChorpIM/BhImY/hy+Z6jekmN92cXGPI1QJasVPYY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32 h1:dpbVNUjczQ8Ae3QKHbpHBpfvaVkRdesxpTOe9pTouhU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.32/go.mod h1:RudqOgadTWdcS3t/erPQo24pcVEoYyqj/kKW5Vya21I=
@ -601,26 +601,26 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.22/go.mod h1:QFVbqK
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1 h1:lRWp3bNu5wy0X3a8GS42JvZFlv++AKsMdzEnoiVJrkg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.1/go.mod h1:VXBHSxdN46bsJrkniN68psSwbyBKsazQfU2yX/iSDso=
github.com/aws/aws-sdk-go-v2/service/kms v1.20.2/go.mod h1:vdqtUOdVuf5ooy+hJ2GnzqNo94xiAA9s1xbZ1hQgRE0=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.8 h1:NLpW2zjplPmay81ZU6SbFZc6oI156PxFvU/zn41WlQE=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.8/go.mod h1:oNFgSKrV6y06CQnFXzcYtEOB/EfRP1aHWbddn1TEXG8=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.9 h1:OvTETycfHkxIYPgff3hHykG1lH03zBx9G7HH9nLEKBY=
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.14.9/go.mod h1:oNFgSKrV6y06CQnFXzcYtEOB/EfRP1aHWbddn1TEXG8=
github.com/aws/aws-sdk-go-v2/service/s3 v1.30.2/go.mod h1:SXDHd6fI2RhqB7vmAzyYQCTQnpZrIprVJvYxpzW3JAM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2 h1:iOZoYePk+EuBI1tC7bxeRjO+JvClcYm2fZYW5WPIOMQ=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.2/go.mod h1:aSl9/LJltSz1cVusiR/Mu8tvI4Sv/5w/WWrJmmkNii0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.3 h1:MG+2UlhyBL3oCOoHbUQh+Sqr3elN0I5PBe0MtVh0xMg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.31.3/go.mod h1:aSl9/LJltSz1cVusiR/Mu8tvI4Sv/5w/WWrJmmkNii0=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.18.3/go.mod h1:hqPcyOuLU6yWIbLy3qMnQnmidgKuIEwqIlW6+chYnog=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.2 h1:mRA8bnA0zdTvsGXmoZ6EOmTTmORjEV1uareB4GfzfK0=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.2/go.mod h1:QNYziZIPDbKmKRoTHi9wkgqVidknyiGHfig1UNOojqk=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.3 h1:bqvkwBuoYZ28Aybq10A9uXh7LkPCh7W4nd3l5bf3v5A=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.19.3/go.mod h1:QNYziZIPDbKmKRoTHi9wkgqVidknyiGHfig1UNOojqk=
github.com/aws/aws-sdk-go-v2/service/sns v1.20.2/go.mod h1:VN2n9SOMS1lNbh5YD7o+ho0/rgfifSrK//YYNiVVF5E=
github.com/aws/aws-sdk-go-v2/service/sqs v1.20.2/go.mod h1:1ttxGjUHZliCQMpPss1sU5+Ph/5NvdMFRzr96bv8gm0=
github.com/aws/aws-sdk-go-v2/service/ssm v1.35.2/go.mod h1:VLSz2SHUKYFSOlXB/GlXoLU6KPYQJAbw7I20TDJdyws=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.1/go.mod h1:IgV8l3sj22nQDd5qcAGY0WenwCzCphqdbFOpfktZPrI=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.7 h1:rrYYhsvcvg6CDDoo4GHKtAWBFutS86CpmGvqHJHYL9w=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.7/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.8 h1:5cb3D6xb006bPTqEfCNaEA6PPEfBXxxy4NNeX/44kGk=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.8/go.mod h1:GNIveDnP+aE3jujyUSH5aZ/rktsTM5EvtKnCqBZawdw=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.1/go.mod h1:O1YSOg3aekZibh2SngvCRRG+cRHKKlYgxf/JBF/Kr/k=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.7 h1:Vjpjt3svuJ/u+eKRfycZwqLsLoxyuvvZyHMJSk+3k58=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.7/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8 h1:NZaj0ngZMzsubWZbrEFSB4rgSQRbFq38Sd6KBxHuOIU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.8/go.mod h1:44qFP1g7pfd+U+sQHLPalAPKnyfTZjJsYR4xIwsJy5o=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.3/go.mod h1:b+psTJn33Q4qGoDaM7ZiOVVG8uVjGI6HaZ8WBHdgDgU=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.8 h1:SQ8pPoXfzuz4DImO4KAi9xhO4ANG0Ckb5clZ5GoRAPw=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.8/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.9 h1:Qf1aWwnsNkyAoqDqmdM3nHwN78XQjec27LjM6b9vyfI=
github.com/aws/aws-sdk-go-v2/service/sts v1.18.9/go.mod h1:yyW88BEPXA2fGFyI2KCcZC3dNpiT0CZAHaF+i656/tQ=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
@ -817,7 +817,6 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@ -1197,6 +1196,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20220318212150-b2ab0324ddda/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.1 h1:XJQvZvUdPzOGdf4ZMQc78wYt9XrdIZOl//n03i8P68Q=
github.com/google/s2a-go v0.1.1/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@ -1390,8 +1391,8 @@ github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf h1:BLqUs5GEGMJ+h9s63INbbwXUXe95wvhvh1esISGgau0=
github.com/jackc/pgx/v5 v5.3.2-0.20230325152211-ca022267dbbf/go.mod h1:sU+RaYl9qnhD3Ce+mwnFii6YEPx70mCYghBzKvqq4qo=
github.com/jackc/pgx/v5 v5.3.2-0.20230411230705-2cf1541bb90a h1:wDHgeTk2JY7s81p4inOAtvzoxk00bvnofbdkisnwsaE=
github.com/jackc/pgx/v5 v5.3.2-0.20230411230705-2cf1541bb90a/go.mod h1:sU+RaYl9qnhD3Ce+mwnFii6YEPx70mCYghBzKvqq4qo=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@ -1749,8 +1750,9 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
github.com/prometheus/client_golang v1.15.0 h1:5fCgGYogn0hFdhyhLbw7hEsWxufKtY9klyvdNfFlFhM=
github.com/prometheus/client_golang v1.15.0/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -1809,15 +1811,16 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors v1.8.3 h1:O+qNyWn7Z+F9M0ILBHgMVPuB1xTOucVd5gtaYyXBpRo=
github.com/rs/cors v1.8.3/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -2659,8 +2662,8 @@ google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/
google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY=
google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI=
google.golang.org/api v0.116.0 h1:09tOPVufPwfm5W4aA8EizGHJ7BcoRDsIareM2a15gO4=
google.golang.org/api v0.116.0/go.mod h1:9cD4/t6uvd9naoEJFA+M96d0IuB6BqFuyhpw68+mRGg=
google.golang.org/api v0.118.0 h1:FNfHq9Z2GKULxu7cEhCaB0wWQHg43UpomrrN+24ZRdE=
google.golang.org/api v0.118.0/go.mod h1:76TtD3vkgmZ66zZzp72bUUklpmQmKlhh6sYtIjYK+5E=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -2805,8 +2808,8 @@ google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ
google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd h1:sLpv7bNL1AsX3fdnWh9WVh7ejIzXdOc1RRHGeAmeStU=
google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=

View file

@ -587,7 +587,7 @@ func (c *BaseConnection) copyFile(virtualSourcePath, virtualTargetPath string, s
if ok, _ := c.User.IsFileAllowed(virtualTargetPath); !ok {
return fmt.Errorf("file %q is not allowed: %w", virtualTargetPath, c.GetPermissionDeniedError())
}
if c.isSameResource(virtualSourcePath, virtualSourcePath) {
if c.isSameResource(virtualSourcePath, virtualTargetPath) {
fs, fsTargetPath, err := c.GetFsAndResolvedPath(virtualTargetPath)
if err != nil {
return err

View file

@ -3428,14 +3428,16 @@ func TestDelayedQuotaUpdater(t *testing.T) {
func TestPasswordCaching(t *testing.T) {
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
assert.NoError(t, err)
found, match := dataprovider.CheckCachedPassword(user.Username, defaultPassword)
dbUser, err := dataprovider.UserExists(user.Username, "")
assert.NoError(t, err)
found, match := dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
user.Password = "wrong"
_, _, err = getSftpClient(user)
assert.Error(t, err)
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword)
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
user.Password = ""
@ -3447,21 +3449,31 @@ func TestPasswordCaching(t *testing.T) {
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword)
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword+"_")
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword+"_", dbUser.Password)
assert.True(t, found)
assert.False(t, match)
found, match = dataprovider.CheckCachedPassword(user.Username+"_", defaultPassword)
found, match = dataprovider.CheckCachedUserPassword(user.Username+"_", defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword)
// the password was not changed
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
// the password hash will change
user.Password = defaultPassword
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
dbUser, err = dataprovider.UserExists(user.Username, "")
assert.NoError(t, err)
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
@ -3472,8 +3484,52 @@ func TestPasswordCaching(t *testing.T) {
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
//change password
newPassword := defaultPassword + "mod"
user.Password = newPassword
_, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
dbUser, err = dataprovider.UserExists(user.Username, "")
assert.NoError(t, err)
found, match = dataprovider.CheckCachedUserPassword(user.Username, newPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword)
conn, client, err = getSftpClient(user)
if assert.NoError(t, err) {
defer conn.Close()
defer client.Close()
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.False(t, match)
found, match = dataprovider.CheckCachedUserPassword(user.Username, newPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
// update the password
err = dataprovider.UpdateUserPassword(user.Username, defaultPassword, "", "", "")
assert.NoError(t, err)
dbUser, err = dataprovider.UserExists(user.Username, "")
assert.NoError(t, err)
// the stored hash does not match
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
user.Password = defaultPassword
conn, client, err = getSftpClient(user)
if assert.NoError(t, err) {
defer conn.Close()
defer client.Close()
err = checkBasicSFTP(client)
assert.NoError(t, err)
}
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
@ -3481,7 +3537,7 @@ func TestPasswordCaching(t *testing.T) {
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
found, match = dataprovider.CheckCachedPassword(user.Username, defaultPassword)
found, match = dataprovider.CheckCachedUserPassword(user.Username, defaultPassword, dbUser.Password)
assert.False(t, found)
assert.False(t, match)
}

View file

@ -424,16 +424,29 @@ func (a *Admin) GetGroupsAsString() string {
// CheckPassword verifies the admin password
func (a *Admin) CheckPassword(password string) (bool, error) {
if config.PasswordCaching {
found, match := cachedAdminPasswords.Check(a.Username, password, a.Password)
if found {
if !match {
return false, ErrInvalidCredentials
}
return match, nil
}
}
if strings.HasPrefix(a.Password, bcryptPwdPrefix) {
if err := bcrypt.CompareHashAndPassword([]byte(a.Password), []byte(password)); err != nil {
return false, ErrInvalidCredentials
}
cachedAdminPasswords.Add(a.Username, password, a.Password)
return true, nil
}
match, err := argon2id.ComparePasswordAndHash(password, a.Password)
if !match || err != nil {
return false, ErrInvalidCredentials
}
if match && err == nil {
cachedAdminPasswords.Add(a.Username, password, a.Password)
}
return match, err
}

View file

@ -185,6 +185,15 @@ func (k *APIKey) Authenticate(plainKey string) error {
return fmt.Errorf("API key %q is expired, expiration timestamp: %v current timestamp: %v", k.KeyID,
k.ExpiresAt, util.GetTimeAsMsSinceEpoch(time.Now()))
}
if config.PasswordCaching {
found, match := cachedAPIKeys.Check(k.KeyID, plainKey, k.Key)
if found {
if !match {
return ErrInvalidCredentials
}
return nil
}
}
if strings.HasPrefix(k.Key, bcryptPwdPrefix) {
if err := bcrypt.CompareHashAndPassword([]byte(k.Key), []byte(plainKey)); err != nil {
return ErrInvalidCredentials
@ -196,5 +205,6 @@ func (k *APIKey) Authenticate(plainKey string) error {
}
}
cachedAPIKeys.Add(k.KeyID, plainKey, k.Key)
return nil
}

View file

@ -15,34 +15,78 @@
package dataprovider
import (
"sort"
"sync"
"sync/atomic"
"time"
"github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/util"
)
var cachedPasswords passwordsCache
var (
cachedUserPasswords credentialsCache
cachedAdminPasswords credentialsCache
cachedAPIKeys credentialsCache
)
func init() {
cachedPasswords = passwordsCache{
cache: make(map[string]string),
cachedUserPasswords = credentialsCache{
name: "users",
sizeLimit: 500,
cache: make(map[string]credentialObject),
}
cachedAdminPasswords = credentialsCache{
name: "admins",
sizeLimit: 100,
cache: make(map[string]credentialObject),
}
cachedAPIKeys = credentialsCache{
name: "API keys",
sizeLimit: 500,
cache: make(map[string]credentialObject),
}
}
type passwordsCache struct {
sync.RWMutex
cache map[string]string
// CheckCachedUserPassword is an utility method used only in test cases
func CheckCachedUserPassword(username, password, hash string) (bool, bool) {
return cachedUserPasswords.Check(username, password, hash)
}
func (c *passwordsCache) Add(username, password string) {
if !config.PasswordCaching || username == "" || password == "" {
type credentialObject struct {
key string
hash string
password string
usedAt *atomic.Int64
}
type credentialsCache struct {
name string
sizeLimit int
sync.RWMutex
cache map[string]credentialObject
}
func (c *credentialsCache) Add(username, password, hash string) {
if !config.PasswordCaching || username == "" || password == "" || hash == "" {
return
}
c.Lock()
defer c.Unlock()
c.cache[username] = password
obj := credentialObject{
key: username,
hash: hash,
password: password,
usedAt: &atomic.Int64{},
}
obj.usedAt.Store(util.GetTimeAsMsSinceEpoch(time.Now()))
c.cache[username] = obj
}
func (c *passwordsCache) Remove(username string) {
func (c *credentialsCache) Remove(username string) {
if !config.PasswordCaching {
return
}
@ -53,24 +97,73 @@ func (c *passwordsCache) Remove(username string) {
delete(c.cache, username)
}
// Check returns if the user is found and if the password match
func (c *passwordsCache) Check(username, password string) (bool, bool) {
if username == "" || password == "" {
// Check returns if the username is found and if the password match
func (c *credentialsCache) Check(username, password, hash string) (bool, bool) {
if username == "" || password == "" || hash == "" {
return false, false
}
c.RLock()
defer c.RUnlock()
pwd, ok := c.cache[username]
creds, ok := c.cache[username]
if !ok {
return false, false
}
return true, pwd == password
if creds.hash != hash {
creds.usedAt.Store(0)
return false, false
}
match := creds.password == password
if match {
creds.usedAt.Store(util.GetTimeAsMsSinceEpoch(time.Now()))
}
return true, match
}
// CheckCachedPassword is an utility method used only in test cases
func CheckCachedPassword(username, password string) (bool, bool) {
return cachedPasswords.Check(username, password)
func (c *credentialsCache) count() int {
c.RLock()
defer c.RUnlock()
return len(c.cache)
}
func (c *credentialsCache) cleanup() {
if !config.PasswordCaching {
return
}
if c.count() <= c.sizeLimit {
return
}
c.Lock()
defer c.Unlock()
for k, v := range c.cache {
if v.usedAt.Load() < util.GetTimeAsMsSinceEpoch(time.Now().Add(-60*time.Minute)) {
delete(c.cache, k)
}
}
providerLog(logger.LevelDebug, "size for credentials %q after cleanup: %d", c.name, len(c.cache))
if len(c.cache) < c.sizeLimit*5 {
return
}
numToRemove := len(c.cache) - c.sizeLimit
providerLog(logger.LevelDebug, "additional item to remove from credentials %q: %d", c.name, numToRemove)
credentials := make([]credentialObject, 0, len(c.cache))
for _, v := range c.cache {
credentials = append(credentials, v)
}
sort.Slice(credentials, func(i, j int) bool {
return credentials[i].usedAt.Load() < credentials[j].usedAt.Load()
})
for idx := range credentials {
if idx >= numToRemove {
break
}
delete(c.cache, credentials[idx].key)
}
providerLog(logger.LevelDebug, "size for credentials %q after additional cleanup: %d", c.name, len(c.cache))
}

View file

@ -1775,6 +1775,7 @@ func DeleteAPIKey(keyID string, executor, ipAddress, role string) error {
err = provider.deleteAPIKey(apiKey)
if err == nil {
executeAction(operationDelete, executor, ipAddress, actionObjectAPIKey, apiKey.KeyID, role, &apiKey)
cachedAPIKeys.Remove(keyID)
}
return err
}
@ -1984,6 +1985,7 @@ func DeleteAdmin(username, executor, ipAddress, role string) error {
err = provider.deleteAdmin(admin)
if err == nil {
executeAction(operationDelete, executor, ipAddress, actionObjectAdmin, admin.Username, role, &admin)
cachedAdminPasswords.Remove(username)
}
return err
}
@ -2057,7 +2059,6 @@ func UpdateUserPassword(username, plainPwd, executor, ipAddress, role string) er
return err
}
webDAVUsersCache.swap(&user)
cachedPasswords.Remove(username)
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, username, role, &user)
return nil
}
@ -2070,7 +2071,6 @@ func UpdateUser(user *User, executor, ipAddress, role string) error {
err := provider.updateUser(user)
if err == nil {
webDAVUsersCache.swap(user)
cachedPasswords.Remove(user.Username)
executeAction(operationUpdate, executor, ipAddress, actionObjectUser, user.Username, role, user)
}
return err
@ -2087,7 +2087,7 @@ func DeleteUser(username, executor, ipAddress, role string) error {
if err == nil {
RemoveCachedWebDAVUser(user.Username)
delayedQuotaUpdater.resetUserQuota(user.Username)
cachedPasswords.Remove(username)
cachedUserPasswords.Remove(username)
executeAction(operationDelete, executor, ipAddress, actionObjectUser, user.Username, role, &user)
}
return err
@ -3062,7 +3062,7 @@ func ValidateUser(user *User) error {
func isPasswordOK(user *User, password string) (bool, error) {
if config.PasswordCaching {
found, match := cachedPasswords.Check(user.Username, password)
found, match := cachedUserPasswords.Check(user.Username, password, user.Password)
if found {
return match, nil
}
@ -3100,7 +3100,7 @@ func isPasswordOK(user *User, password string) (bool, error) {
match = fmt.Sprintf("%s%x", md5LDAPPwdPrefix, h.Sum(nil)) == user.Password
}
if err == nil && match {
cachedPasswords.Add(user.Username, password)
cachedUserPasswords.Add(user.Username, password, user.Password)
if updatePwd {
convertUserPassword(user.Username, password)
}
@ -3806,7 +3806,6 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
}
userID := u.ID
userPwd := u.Password
userUsedQuotaSize := u.UsedQuotaSize
userUsedQuotaFiles := u.UsedQuotaFiles
userUsedDownloadTransfer := u.UsedDownloadDataTransfer
@ -3844,9 +3843,6 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
err = provider.updateUser(&u)
if err == nil {
webDAVUsersCache.swap(&u)
if u.Password != userPwd {
cachedPasswords.Remove(username)
}
}
}
if err != nil {
@ -4081,7 +4077,7 @@ func doExternalAuth(username, password string, pubKey []byte, keyboardInteractiv
err = provider.updateUser(&user)
if err == nil {
webDAVUsersCache.swap(&user)
cachedPasswords.Add(user.Username, password)
cachedUserPasswords.Add(user.Username, password, user.Password)
}
return user, err
}
@ -4154,7 +4150,7 @@ func doPluginAuth(username, password string, pubKey []byte, ip, protocol string,
err = provider.updateUser(&user)
if err == nil {
webDAVUsersCache.swap(&user)
cachedPasswords.Add(user.Username, password)
cachedUserPasswords.Add(user.Username, password, user.Password)
}
return user, err
}

View file

@ -100,6 +100,9 @@ func checkDataprovider() {
func checkCacheUpdates() {
checkUserCache()
checkIPListEntryCache()
cachedUserPasswords.cleanup()
cachedAdminPasswords.cleanup()
cachedAPIKeys.cleanup()
}
func checkUserCache() {
@ -124,11 +127,11 @@ func checkUserCache() {
go provider.deleteUser(user, false) //nolint:errcheck
}
webDAVUsersCache.remove(user.Username)
cachedUserPasswords.Remove(user.Username)
delayedQuotaUpdater.resetUserQuota(user.Username)
} else {
webDAVUsersCache.swap(&user)
}
cachedPasswords.Remove(user.Username)
}
lastUserCacheUpdate.Store(checkTime)
providerLog(logger.LevelDebug, "end user cache check, new update time %v", util.GetTimeFromMsecSinceEpoch(lastUserCacheUpdate.Load()))

View file

@ -3900,7 +3900,9 @@ func TestLoginExternalAuth(t *testing.T) {
assert.NoError(t, checkBasicSFTP(client))
}
if !usePubKey {
found, match := dataprovider.CheckCachedPassword(defaultUsername, defaultPassword)
dbUser, err := dataprovider.UserExists(defaultUsername, "")
assert.NoError(t, err)
found, match := dataprovider.CheckCachedUserPassword(defaultUsername, defaultPassword, dbUser.Password)
assert.True(t, found)
assert.True(t, match)
}

View file

@ -1,4 +1,4 @@
/*! Bootstrap integration for DataTables' Buttons
* ©2016 SpryMedia Ltd - datatables.net/license
*/
!function(e){"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs4","datatables.net-buttons"],function(t){return e(t,window,document)}):"object"==typeof exports?module.exports=function(t,n){return t=t||window,(n=n||("undefined"!=typeof window?require("jquery"):require("jquery")(t))).fn.dataTable||require("datatables.net-bs4")(t,n),n.fn.dataTable.Buttons||require("datatables.net-buttons")(t,n),e(n,0,t.document)}:e(jQuery,window,document)}(function(e,t,n,o){"use strict";var a=e.fn.dataTable;return e.extend(!0,a.Buttons.defaults,{dom:{container:{className:"dt-buttons btn-group flex-wrap"},button:{className:"btn btn-secondary"},collection:{tag:"div",className:"dropdown-menu",closeButton:!1,button:{tag:"a",className:"dt-button dropdown-item",active:"active",disabled:"disabled"}},splitWrapper:{tag:"div",className:"dt-btn-split-wrapper btn-group",closeButton:!1},splitDropdown:{tag:"button",text:"",className:"btn btn-secondary dt-btn-split-drop dropdown-toggle dropdown-toggle-split",closeButton:!1,align:"split-left",splitAlignClass:"dt-button-split-left"},splitDropdownButton:{tag:"button",className:"dt-btn-split-drop-button btn btn-secondary",closeButton:!1}},buttonCreated:function(t,n){return t.buttons?e('<div class="btn-group"/>').append(n):n}}),a.ext.buttons.collection.className+=" dropdown-toggle",a.ext.buttons.collection.rightAlignClassName="dropdown-menu-right",a});
!function(e){var o,a;"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs4","datatables.net-buttons"],function(t){return e(t,window,document)}):"object"==typeof exports?(o=require("jquery"),a=function(t,n){n.fn.dataTable||require("datatables.net-bs4")(t,n),n.fn.dataTable.Buttons||require("datatables.net-buttons")(t,n)},"undefined"!=typeof window?module.exports=function(t,n){return t=t||window,n=n||o(t),a(t,n),e(n,0,t.document)}:(a(window,o),module.exports=e(o,window,window.document))):e(jQuery,window,document)}(function(e,t,n,o){"use strict";var a=e.fn.dataTable;return e.extend(!0,a.Buttons.defaults,{dom:{container:{className:"dt-buttons btn-group flex-wrap"},button:{className:"btn btn-secondary"},collection:{tag:"div",className:"dropdown-menu",closeButton:!1,button:{tag:"a",className:"dt-button dropdown-item",active:"active",disabled:"disabled"}},splitWrapper:{tag:"div",className:"dt-btn-split-wrapper btn-group",closeButton:!1},splitDropdown:{tag:"button",text:"",className:"btn btn-secondary dt-btn-split-drop dropdown-toggle dropdown-toggle-split",closeButton:!1,align:"split-left",splitAlignClass:"dt-button-split-left"},splitDropdownButton:{tag:"button",className:"dt-btn-split-drop-button btn btn-secondary",closeButton:!1}},buttonCreated:function(t,n){return t.buttons?e('<div class="btn-group"/>').append(n):n}}),a.ext.buttons.collection.className+=" dropdown-toggle",a.ext.buttons.collection.rightAlignClassName="dropdown-menu-right",a});

View file

@ -2,4 +2,4 @@
* Column visibility buttons for Buttons and DataTables.
* 2016 SpryMedia Ltd - datatables.net/license
*/
!function(t){"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(n){return t(n,window,document)}):"object"==typeof exports?module.exports=function(n,e){return n=n||window,(e=e||("undefined"!=typeof window?require("jquery"):require("jquery")(n))).fn.dataTable||require("datatables.net")(n,e),e.fn.dataTable.Buttons||require("datatables.net-buttons")(n,e),t(e,0,n.document)}:t(jQuery,window,document)}(function(n,e,t,l){"use strict";var o=n.fn.dataTable;return n.extend(o.ext.buttons,{colvis:function(o,i){var l=null,n={extend:"collection",init:function(n,e){l=e},text:function(n){return n.i18n("buttons.colvis","Column visibility")},className:"buttons-colvis",closeButton:!1,buttons:[{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}]};return o.on("column-reorder.dt"+i.namespace,function(n,e,t){o.button(null,o.button(null,l).node()).collectionRebuild([{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}])}),n},columnsToggle:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnToggle",columns:n,columnText:e.columnText}}).toArray()},columnToggle:function(n,e){return{extend:"columnVisibility",columns:e.columns,columnText:e.columnText}},columnsVisibility:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnVisibility",columns:n,visibility:e.visibility,columnText:e.columnText}}).toArray()},columnVisibility:{columns:l,text:function(n,e,t){return t._columnText(n,t)},className:"buttons-columnVisibility",action:function(n,e,t,o){var e=e.columns(o.columns),i=e.visible();e.visible(o.visibility!==l?o.visibility:!(i.length&&i[0]))},init:function(o,n,i){var l=this;n.attr("data-cv-idx",i.columns),o.on("column-visibility.dt"+i.namespace,function(n,e){e.bDestroying||e.nTable!=o.settings()[0].nTable||l.active(o.column(i.columns).visible())}).on("column-reorder.dt"+i.namespace,function(n,e,t){i.destroying||1===o.columns(i.columns).count()&&(l.text(i._columnText(o,i)),l.active(o.column(i.columns).visible()))}),this.active(o.column(i.columns).visible())},destroy:function(n,e,t){n.off("column-visibility.dt"+t.namespace).off("column-reorder.dt"+t.namespace)},_columnText:function(n,e){var t=n.column(e.columns).index(),o=n.settings()[0].aoColumns[t].sTitle;return o=(o=o||n.column(t).header().innerHTML).replace(/\n/g," ").replace(/<br\s*\/?>/gi," ").replace(/<select(.*?)<\/select>/g,"").replace(/<!\-\-.*?\-\->/g,"").replace(/<.*?>/g,"").replace(/^\s+|\s+$/g,""),e.columnText?e.columnText(n,t,o):o}},colvisRestore:{className:"buttons-colvisRestore",text:function(n){return n.i18n("buttons.colvisRestore","Restore visibility")},init:function(e,n,t){t._visOriginal=e.columns().indexes().map(function(n){return e.column(n).visible()}).toArray()},action:function(n,e,t,o){e.columns().every(function(n){n=e.colReorder&&e.colReorder.transpose?e.colReorder.transpose(n,"toOriginal"):n;this.visible(o._visOriginal[n])})}},colvisGroup:{className:"buttons-colvisGroup",action:function(n,e,t,o){e.columns(o.show).visible(!0,!1),e.columns(o.hide).visible(!1,!1),e.columns.adjust()},show:[],hide:[]}}),o});
!function(t){var o,i;"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(n){return t(n,window,document)}):"object"==typeof exports?(o=require("jquery"),i=function(n,e){e.fn.dataTable||require("datatables.net")(n,e),e.fn.dataTable.Buttons||require("datatables.net-buttons")(n,e)},"undefined"!=typeof window?module.exports=function(n,e){return n=n||window,e=e||o(n),i(n,e),t(e,0,n.document)}:(i(window,o),module.exports=t(o,window,window.document))):t(jQuery,window,document)}(function(n,e,t,l){"use strict";var o=n.fn.dataTable;return n.extend(o.ext.buttons,{colvis:function(o,i){var l=null,n={extend:"collection",init:function(n,e){l=e},text:function(n){return n.i18n("buttons.colvis","Column visibility")},className:"buttons-colvis",closeButton:!1,buttons:[{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}]};return o.on("column-reorder.dt"+i.namespace,function(n,e,t){o.button(null,o.button(null,l).node()).collectionRebuild([{extend:"columnsToggle",columns:i.columns,columnText:i.columnText}])}),n},columnsToggle:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnToggle",columns:n,columnText:e.columnText}}).toArray()},columnToggle:function(n,e){return{extend:"columnVisibility",columns:e.columns,columnText:e.columnText}},columnsVisibility:function(n,e){return n.columns(e.columns).indexes().map(function(n){return{extend:"columnVisibility",columns:n,visibility:e.visibility,columnText:e.columnText}}).toArray()},columnVisibility:{columns:l,text:function(n,e,t){return t._columnText(n,t)},className:"buttons-columnVisibility",action:function(n,e,t,o){var e=e.columns(o.columns),i=e.visible();e.visible(o.visibility!==l?o.visibility:!(i.length&&i[0]))},init:function(o,n,i){var l=this;n.attr("data-cv-idx",i.columns),o.on("column-visibility.dt"+i.namespace,function(n,e){e.bDestroying||e.nTable!=o.settings()[0].nTable||l.active(o.column(i.columns).visible())}).on("column-reorder.dt"+i.namespace,function(n,e,t){i.destroying||1===o.columns(i.columns).count()&&(l.text(i._columnText(o,i)),l.active(o.column(i.columns).visible()))}),this.active(o.column(i.columns).visible())},destroy:function(n,e,t){n.off("column-visibility.dt"+t.namespace).off("column-reorder.dt"+t.namespace)},_columnText:function(n,e){var t=n.column(e.columns).index(),o=n.settings()[0].aoColumns[t].sTitle;return o=(o=o||n.column(t).header().innerHTML).replace(/\n/g," ").replace(/<br\s*\/?>/gi," ").replace(/<select(.*?)<\/select>/g,"").replace(/<!\-\-.*?\-\->/g,"").replace(/<.*?>/g,"").replace(/^\s+|\s+$/g,""),e.columnText?e.columnText(n,t,o):o}},colvisRestore:{className:"buttons-colvisRestore",text:function(n){return n.i18n("buttons.colvisRestore","Restore visibility")},init:function(e,n,t){t._visOriginal=e.columns().indexes().map(function(n){return e.column(n).visible()}).toArray()},action:function(n,e,t,o){e.columns().every(function(n){n=e.colReorder&&e.colReorder.transpose?e.colReorder.transpose(n,"toOriginal"):n;this.visible(o._visOriginal[n])})}},colvisGroup:{className:"buttons-colvisGroup",action:function(n,e,t,o){e.columns(o.show).visible(!0,!1),e.columns(o.hide).visible(!1,!1),e.columns.adjust()},show:[],hide:[]}}),o});

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
/*! DataTables Bootstrap 4 integration
* ©2011-2017 SpryMedia Ltd - datatables.net/license
*/
!function(t){"function"==typeof define&&define.amd?define(["jquery","datatables.net"],function(e){return t(e,window,document)}):"object"==typeof exports?module.exports=function(e,a){return e=e||window,(a=a||("undefined"!=typeof window?require("jquery"):require("jquery")(e))).fn.dataTable||require("datatables.net")(e,a),t(a,0,e.document)}:t(jQuery,window,document)}(function(x,e,n,r){"use strict";var s=x.fn.dataTable;return x.extend(!0,s.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",renderer:"bootstrap"}),x.extend(s.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap4",sFilterInput:"form-control form-control-sm",sLengthSelect:"custom-select custom-select-sm form-control form-control-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"}),s.ext.renderer.pageButton.bootstrap=function(i,e,d,a,l,c){function u(e,a){for(var t,n,r=function(e){e.preventDefault(),x(e.currentTarget).hasClass("disabled")||b.page()==e.data.action||b.page(e.data.action).draw("page")},s=0,o=a.length;s<o;s++)if(t=a[s],Array.isArray(t))u(e,t);else{switch(f=p="",t){case"ellipsis":p="&#x2026;",f="disabled";break;case"first":p=g.sFirst,f=t+(0<l?"":" disabled");break;case"previous":p=g.sPrevious,f=t+(0<l?"":" disabled");break;case"next":p=g.sNext,f=t+(l<c-1?"":" disabled");break;case"last":p=g.sLast,f=t+(l<c-1?"":" disabled");break;default:p=t+1,f=l===t?"active":""}p&&(n=-1!==f.indexOf("disabled"),n=x("<li>",{class:m.sPageButton+" "+f,id:0===d&&"string"==typeof t?i.sTableId+"_"+t:null}).append(x("<a>",{href:n?null:"#","aria-controls":i.sTableId,"aria-disabled":n?"true":null,"aria-label":w[t],"aria-role":"link","aria-current":"active"===f?"page":null,"data-dt-idx":t,tabindex:i.iTabIndex,class:"page-link"}).html(p)).appendTo(e),i.oApi._fnBindAction(n,{action:t},r))}}var p,f,t,b=new s.Api(i),m=i.oClasses,g=i.oLanguage.oPaginate,w=i.oLanguage.oAria.paginate||{};try{t=x(e).find(n.activeElement).data("dt-idx")}catch(e){}u(x(e).empty().html('<ul class="pagination"/>').children("ul"),a),t!==r&&x(e).find("[data-dt-idx="+t+"]").trigger("focus")},s});
!function(t){var n,o;"function"==typeof define&&define.amd?define(["jquery","datatables.net"],function(e){return t(e,window,document)}):"object"==typeof exports?(n=require("jquery"),o=function(e,a){a.fn.dataTable||require("datatables.net")(e,a)},"undefined"!=typeof window?module.exports=function(e,a){return e=e||window,a=a||n(e),o(e,a),t(a,0,e.document)}:(o(window,n),module.exports=t(n,window,window.document))):t(jQuery,window,document)}(function(x,e,n,o){"use strict";var r=x.fn.dataTable;return x.extend(!0,r.defaults,{dom:"<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",renderer:"bootstrap"}),x.extend(r.ext.classes,{sWrapper:"dataTables_wrapper dt-bootstrap4",sFilterInput:"form-control form-control-sm",sLengthSelect:"custom-select custom-select-sm form-control form-control-sm",sProcessing:"dataTables_processing card",sPageButton:"paginate_button page-item"}),r.ext.renderer.pageButton.bootstrap=function(i,e,d,a,l,c){function u(e,a){for(var t,n,o=function(e){e.preventDefault(),x(e.currentTarget).hasClass("disabled")||m.page()==e.data.action||m.page(e.data.action).draw("page")},r=0,s=a.length;r<s;r++)if(t=a[r],Array.isArray(t))u(e,t);else{switch(f=p="",t){case"ellipsis":p="&#x2026;",f="disabled";break;case"first":p=g.sFirst,f=t+(0<l?"":" disabled");break;case"previous":p=g.sPrevious,f=t+(0<l?"":" disabled");break;case"next":p=g.sNext,f=t+(l<c-1?"":" disabled");break;case"last":p=g.sLast,f=t+(l<c-1?"":" disabled");break;default:p=t+1,f=l===t?"active":""}p&&(n=-1!==f.indexOf("disabled"),n=x("<li>",{class:b.sPageButton+" "+f,id:0===d&&"string"==typeof t?i.sTableId+"_"+t:null}).append(x("<a>",{href:n?null:"#","aria-controls":i.sTableId,"aria-disabled":n?"true":null,"aria-label":w[t],"aria-role":"link","aria-current":"active"===f?"page":null,"data-dt-idx":t,tabindex:i.iTabIndex,class:"page-link"}).html(p)).appendTo(e),i.oApi._fnBindAction(n,{action:t},o))}}var p,f,t,m=new r.Api(i),b=i.oClasses,g=i.oLanguage.oPaginate,w=i.oLanguage.oAria.paginate||{};try{t=x(e).find(n.activeElement).data("dt-idx")}catch(e){}u(x(e).empty().html('<ul class="pagination"/>').children("ul"),a),t!==o&&x(e).find("[data-dt-idx="+t+"]").trigger("focus")},r});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -3,25 +3,39 @@
(function( factory ){
if ( typeof define === 'function' && define.amd ) {
// AMD
define( ['datatables.net'], function ( $ ) {
define( ['jquery', 'datatables.net'], function ( $ ) {
return factory( $, window, document );
} );
}
else if ( typeof exports === 'object' ) {
// CommonJS
module.exports = function (root, $) {
if ( ! root ) {
// CommonJS environments without a window global must pass a
// root. This will give an error otherwise
root = window;
}
var jq = require('jquery');
var cjsRequires = function (root, $) {
if ( ! $.fn.dataTable ) {
require('datatables.net')(root, $);
}
return factory( $, root, root.document );
};
if (typeof window !== 'undefined') {
module.exports = function (root, $) {
if ( ! root ) {
// CommonJS environments without a window global must pass a
// root. This will give an error otherwise
root = window;
}
if ( ! $ ) {
$ = jq( root );
}
cjsRequires( root, $ );
return factory( $, root, root.document );
};
}
else {
cjsRequires( window, jq );
module.exports = factory( jq, window, window.document );
}
}
else {
// Browser

File diff suppressed because one or more lines are too long

View file

@ -1,4 +1,4 @@
/*! Bootstrap 4 integration for DataTables' Responsive
* © SpryMedia Ltd - datatables.net/license
*/
!function(a){"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs4","datatables.net-responsive"],function(e){return a(e,window,document)}):"object"==typeof exports?module.exports=function(e,d){return e=e||window,(d=d||("undefined"!=typeof window?require("jquery"):require("jquery")(e))).fn.dataTable||require("datatables.net-bs4")(e,d),d.fn.dataTable||require("datatables.net-responsive")(e,d),a(d,0,e.document)}:a(jQuery,window,document)}(function(i,e,d,a){"use strict";var n=i.fn.dataTable,t=n.Responsive.display,s=t.modal,l=i('<div class="modal fade dtr-bs-modal" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"/></div></div></div>');return t.modal=function(o){return function(e,d,a){var n,t;i.fn.modal?d||(o&&o.header&&(t=(n=l.find("div.modal-header")).find("button").detach(),n.empty().append('<h4 class="modal-title">'+o.header(e)+"</h4>").append(t)),l.find("div.modal-body").empty().append(a()),l.appendTo("body").modal()):s(e,d,a)}},n});
!function(a){var n,o;"function"==typeof define&&define.amd?define(["jquery","datatables.net-bs4","datatables.net-responsive"],function(e){return a(e,window,document)}):"object"==typeof exports?(n=require("jquery"),o=function(e,d){d.fn.dataTable||require("datatables.net-bs4")(e,d),d.fn.dataTable.Responsive||require("datatables.net-responsive")(e,d)},"undefined"!=typeof window?module.exports=function(e,d){return e=e||window,d=d||n(e),o(e,d),a(d,0,e.document)}:(o(window,n),module.exports=a(n,window,window.document))):a(jQuery,window,document)}(function(i,e,d,a){"use strict";var n=i.fn.dataTable,o=n.Responsive.display,s=o.modal,l=i('<div class="modal fade dtr-bs-modal" role="dialog"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button></div><div class="modal-body"/></div></div></div>');return o.modal=function(t){return function(e,d,a){var n,o;i.fn.modal?d||(t&&t.header&&(o=(n=l.find("div.modal-header")).find("button").detach(),n.empty().append('<h4 class="modal-title">'+t.header(e)+"</h4>").append(o)),l.find("div.modal-body").empty().append(a()),l.appendTo("body").modal()):s(e,d,a)}},n});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long