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:
parent
6279216c2e
commit
3cb53b2c33
25 changed files with 322 additions and 131 deletions
|
@ -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
35
go.mod
|
@ -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
71
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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});
|
|
@ -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
|
@ -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="…",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="…",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
20
static/vendor/datatables/ellipsis.js
vendored
20
static/vendor/datatables/ellipsis.js
vendored
|
@ -3,12 +3,20 @@
|
|||
(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
|
||||
var jq = require('jquery');
|
||||
var cjsRequires = function (root, $) {
|
||||
if ( ! $.fn.dataTable ) {
|
||||
require('datatables.net')(root, $);
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
module.exports = function (root, $) {
|
||||
if ( ! root ) {
|
||||
// CommonJS environments without a window global must pass a
|
||||
|
@ -16,13 +24,19 @@
|
|||
root = window;
|
||||
}
|
||||
|
||||
if ( ! $.fn.dataTable ) {
|
||||
require('datatables.net')(root, $);
|
||||
if ( ! $ ) {
|
||||
$ = jq( root );
|
||||
}
|
||||
|
||||
cjsRequires( root, $ );
|
||||
return factory( $, root, root.document );
|
||||
};
|
||||
}
|
||||
else {
|
||||
cjsRequires( window, jq );
|
||||
module.exports = factory( jq, window, window.document );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Browser
|
||||
factory( jQuery, window, document );
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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">×</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">×</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});
|
2
static/vendor/video-js/video-js.min.css
vendored
2
static/vendor/video-js/video-js.min.css
vendored
File diff suppressed because one or more lines are too long
12
static/vendor/video-js/video.min.js
vendored
12
static/vendor/video-js/video.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue