diff --git a/go.mod b/go.mod
index cc763a09c6..44f05c8c89 100644
--- a/go.mod
+++ b/go.mod
@@ -350,3 +350,5 @@ require (
replace github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20231207143248-4d424e3ae348
+
+replace github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159
diff --git a/go.sum b/go.sum
index 6689ddd529..0d2d4b43d3 100644
--- a/go.sum
+++ b/go.sum
@@ -827,6 +827,8 @@ github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWX
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
+github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159 h1:m63hhLqbqmLGGPtyTtjTdxae61d9tMbRdKvMaDHWcDs=
+github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
@@ -2000,8 +2002,6 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 h1:Wd8WDEEusB5+En4PiRWJp1cP59QLNsQun+mOTW8+s6s=
-github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
diff --git a/services/frontend/pkg/config/config.go b/services/frontend/pkg/config/config.go
index d4bb9c62c1..42262ebf51 100644
--- a/services/frontend/pkg/config/config.go
+++ b/services/frontend/pkg/config/config.go
@@ -142,6 +142,7 @@ type OCS struct {
ListOCMShares bool `yaml:"list_ocm_shares" env:"FRONTEND_OCS_LIST_OCM_SHARES" desc:"Include OCM shares when listing shares. See the OCM service documentation for more details."`
PublicShareMustHavePassword bool `yaml:"public_sharing_share_must_have_password" env:"OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on all public shares."`
WriteablePublicShareMustHavePassword bool `yaml:"public_sharing_writeableshare_must_have_password" env:"OCIS_SHARING_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD;FRONTEND_OCS_PUBLIC_WRITEABLE_SHARE_MUST_HAVE_PASSWORD" desc:"Set this to true if you want to enforce passwords on Uploader, Editor or Contributor shares."`
+ IncludeOCMSharees bool `yaml:"include_ocm_sharees" env:"FRONTEND_OCS_INCLUDE_OCM_SHAREES" desc:"Include OCM sharees when listing sharees."`
}
type CacheWarmupDrivers struct {
diff --git a/services/frontend/pkg/config/defaults/defaultconfig.go b/services/frontend/pkg/config/defaults/defaultconfig.go
index b589aa2f97..1ef32c6710 100644
--- a/services/frontend/pkg/config/defaults/defaultconfig.go
+++ b/services/frontend/pkg/config/defaults/defaultconfig.go
@@ -116,6 +116,7 @@ func DefaultConfig() *config.Config {
StatCacheTTL: 300 * time.Second,
ListOCMShares: true,
PublicShareMustHavePassword: true,
+ IncludeOCMSharees: false,
},
Middleware: config.Middleware{
Auth: config.Auth{
diff --git a/services/frontend/pkg/revaconfig/config.go b/services/frontend/pkg/revaconfig/config.go
index be692a6d9a..38dadd16b2 100644
--- a/services/frontend/pkg/revaconfig/config.go
+++ b/services/frontend/pkg/revaconfig/config.go
@@ -347,6 +347,7 @@ func FrontendConfigFromStruct(cfg *config.Config, logger log.Logger) (map[string
"productversion": version.GetString(),
},
},
+ "include_ocm_sharees": cfg.OCS.IncludeOCMSharees,
},
},
},
diff --git a/vendor/github.com/studio-b12/gowebdav/Makefile b/vendor/github.com/studio-b12/gowebdav/Makefile
index da4be259d3..48dbb4e723 100644
--- a/vendor/github.com/studio-b12/gowebdav/Makefile
+++ b/vendor/github.com/studio-b12/gowebdav/Makefile
@@ -11,22 +11,31 @@ ${BIN}: ${SRC}
test:
go test -modfile=go_test.mod -v -short -cover ./...
-api:
+api: .go/bin/godoc2md
@sed '/^## API$$/,$$d' -i README.md
@echo '## API' >> README.md
- @godoc2md github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\
+ @$< github.com/studio-b12/gowebdav | sed '/^$$/N;/^\n$$/D' |\
sed '2d' |\
sed 's/\/src\/github.com\/studio-b12\/gowebdav\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
sed 's/\/src\/target\//https:\/\/github.com\/studio-b12\/gowebdav\/blob\/master\//g' |\
sed 's/^#/##/g' >> README.md
-check:
+check: .go/bin/gocyclo
gofmt -w -s $(SRC)
@echo
- gocyclo -over 15 .
+ .go/bin/gocyclo -over 15 .
@echo
go vet -modfile=go_test.mod ./...
+
+.go/bin/godoc2md:
+ @mkdir -p $(@D)
+ @GOPATH="$(CURDIR)/.go" go install github.com/davecheney/godoc2md@latest
+
+.go/bin/gocyclo:
+ @mkdir -p $(@D)
+ @GOPATH="$(CURDIR)/.go" go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
+
clean:
@rm -f ${BIN}
diff --git a/vendor/github.com/studio-b12/gowebdav/README.md b/vendor/github.com/studio-b12/gowebdav/README.md
index 50fc0d8567..5355bdbbc2 100644
--- a/vendor/github.com/studio-b12/gowebdav/README.md
+++ b/vendor/github.com/studio-b12/gowebdav/README.md
@@ -5,11 +5,12 @@
[](https://godoc.org/github.com/studio-b12/gowebdav)
[](https://goreportcard.com/report/github.com/studio-b12/gowebdav)
-A golang WebDAV client library.
+A pure Golang WebDAV client library that comes with a [reference implementation](https://github.com/studio-b12/gowebdav/tree/master/cmd/gowebdav).
-## Main features
+## Features at a glance
+
+Our `gowebdav` library allows to perform following actions on the remote WebDAV server:
-`gowebdav` library allows to perform following actions on the remote WebDAV server:
* [create path](#create-path-on-a-webdav-server)
* [get files list](#get-files-list)
* [download file](#download-file-to-byte-array)
@@ -19,6 +20,17 @@ A golang WebDAV client library.
* [copy file to another location](#copy-file-to-another-location)
* [delete file](#delete-file)
+It also provides an [authentication API](#type-authenticator) that makes it easy to encapsulate and control complex authentication challenges.
+The default implementation negotiates the algorithm based on the user's preferences and the methods offered by the remote server.
+
+Out-of-box authentication support for:
+
+* [BasicAuth](https://en.wikipedia.org/wiki/Basic_access_authentication)
+* [DigestAuth](https://en.wikipedia.org/wiki/Digest_access_authentication)
+* [MS-PASS](https://github.com/studio-b12/gowebdav/pull/70#issuecomment-1421713726)
+* [WIP Kerberos](https://github.com/studio-b12/gowebdav/pull/71#issuecomment-1416465334)
+* [WIP Bearer Token](https://github.com/studio-b12/gowebdav/issues/61)
+
## Usage
First of all you should create `Client` instance using `NewClient()` function:
@@ -29,11 +41,13 @@ user := "user"
password := "password"
c := gowebdav.NewClient(root, user, password)
+c.Connect()
+// kick of your work!
```
After you can use this `Client` to perform actions, described below.
-**NOTICE:** we will not check errors in examples, to focus you on the `gowebdav` library's code, but you should do it in your code!
+**NOTICE:** We will not check for errors in the examples, to focus you on the `gowebdav` library's code, but you should do it in your code!
### Create path on a WebDAV server
```go
@@ -59,7 +73,7 @@ webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
bytes, _ := c.Read(webdavFilePath)
-ioutil.WriteFile(localFilePath, bytes, 0644)
+os.WriteFile(localFilePath, bytes, 0644)
```
### Download file via reader
@@ -81,7 +95,7 @@ io.Copy(file, reader)
webdavFilePath := "folder/subfolder/file.txt"
localFilePath := "/tmp/webdav/file.txt"
-bytes, _ := ioutil.ReadFile(localFilePath)
+bytes, _ := os.ReadFile(localFilePath)
c.Write(webdavFilePath, bytes, 0644)
```
@@ -161,21 +175,34 @@ Package gowebdav is a WebDAV client library with a command line tool
included.
### Index
+* [Constants](#pkg-constants)
+* [Variables](#pkg-variables)
* [func FixSlash(s string) string](#FixSlash)
* [func FixSlashes(s string) string](#FixSlashes)
* [func IsErrCode(err error, code int) bool](#IsErrCode)
* [func IsErrNotFound(err error) bool](#IsErrNotFound)
* [func Join(path0 string, path1 string) string](#Join)
+* [func NewPathError(op string, path string, statusCode int) error](#NewPathError)
+* [func NewPathErrorErr(op string, path string, err error) error](#NewPathErrorErr)
* [func PathEscape(path string) string](#PathEscape)
* [func ReadConfig(uri, netrc string) (string, string)](#ReadConfig)
* [func String(r io.Reader) string](#String)
+* [type AuthFactory](#AuthFactory)
* [type Authenticator](#Authenticator)
+ * [func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error)](#NewDigestAuth)
+ * [func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error)](#NewPassportAuth)
+* [type Authorizer](#Authorizer)
+ * [func NewAutoAuth(login string, secret string) Authorizer](#NewAutoAuth)
+ * [func NewEmptyAuth() Authorizer](#NewEmptyAuth)
+ * [func NewPreemptiveAuth(auth Authenticator) Authorizer](#NewPreemptiveAuth)
* [type BasicAuth](#BasicAuth)
- * [func (b *BasicAuth) Authorize(req *http.Request, method string, path string)](#BasicAuth.Authorize)
- * [func (b *BasicAuth) Pass() string](#BasicAuth.Pass)
- * [func (b *BasicAuth) Type() string](#BasicAuth.Type)
- * [func (b *BasicAuth) User() string](#BasicAuth.User)
+ * [func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#BasicAuth.Authorize)
+ * [func (b *BasicAuth) Clone() Authenticator](#BasicAuth.Clone)
+ * [func (b *BasicAuth) Close() error](#BasicAuth.Close)
+ * [func (b *BasicAuth) String() string](#BasicAuth.String)
+ * [func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#BasicAuth.Verify)
* [type Client](#Client)
+ * [func NewAuthClient(uri string, auth Authorizer) *Client](#NewAuthClient)
* [func NewClient(uri, user, pw string) *Client](#NewClient)
* [func (c *Client) Connect() error](#Client.Connect)
* [func (c *Client) Copy(oldpath, newpath string, overwrite bool) error](#Client.Copy)
@@ -190,16 +217,18 @@ included.
* [func (c *Client) Rename(oldpath, newpath string, overwrite bool) error](#Client.Rename)
* [func (c *Client) SetHeader(key, value string)](#Client.SetHeader)
* [func (c *Client) SetInterceptor(interceptor func(method string, rq *http.Request))](#Client.SetInterceptor)
+ * [func (c *Client) SetJar(jar http.CookieJar)](#Client.SetJar)
* [func (c *Client) SetTimeout(timeout time.Duration)](#Client.SetTimeout)
* [func (c *Client) SetTransport(transport http.RoundTripper)](#Client.SetTransport)
* [func (c *Client) Stat(path string) (os.FileInfo, error)](#Client.Stat)
* [func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error)](#Client.Write)
* [func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error)](#Client.WriteStream)
* [type DigestAuth](#DigestAuth)
- * [func (d *DigestAuth) Authorize(req *http.Request, method string, path string)](#DigestAuth.Authorize)
- * [func (d *DigestAuth) Pass() string](#DigestAuth.Pass)
- * [func (d *DigestAuth) Type() string](#DigestAuth.Type)
- * [func (d *DigestAuth) User() string](#DigestAuth.User)
+ * [func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#DigestAuth.Authorize)
+ * [func (d *DigestAuth) Clone() Authenticator](#DigestAuth.Clone)
+ * [func (d *DigestAuth) Close() error](#DigestAuth.Close)
+ * [func (d *DigestAuth) String() string](#DigestAuth.String)
+ * [func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#DigestAuth.Verify)
* [type File](#File)
* [func (f File) ContentType() string](#File.ContentType)
* [func (f File) ETag() string](#File.ETag)
@@ -211,11 +240,12 @@ included.
* [func (f File) Size() int64](#File.Size)
* [func (f File) String() string](#File.String)
* [func (f File) Sys() interface{}](#File.Sys)
-* [type NoAuth](#NoAuth)
- * [func (n *NoAuth) Authorize(req *http.Request, method string, path string)](#NoAuth.Authorize)
- * [func (n *NoAuth) Pass() string](#NoAuth.Pass)
- * [func (n *NoAuth) Type() string](#NoAuth.Type)
- * [func (n *NoAuth) User() string](#NoAuth.User)
+* [type PassportAuth](#PassportAuth)
+ * [func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error](#PassportAuth.Authorize)
+ * [func (p *PassportAuth) Clone() Authenticator](#PassportAuth.Clone)
+ * [func (p *PassportAuth) Close() error](#PassportAuth.Close)
+ * [func (p *PassportAuth) String() string](#PassportAuth.String)
+ * [func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)](#PassportAuth.Verify)
* [type StatusError](#StatusError)
* [func (se StatusError) Error() string](#StatusError.Error)
@@ -223,7 +253,24 @@ included.
* [PathEscape](#example_PathEscape)
##### Package files
-[basicAuth.go](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go) [client.go](https://github.com/studio-b12/gowebdav/blob/master/client.go) [digestAuth.go](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go) [doc.go](https://github.com/studio-b12/gowebdav/blob/master/doc.go) [errors.go](https://github.com/studio-b12/gowebdav/blob/master/errors.go) [file.go](https://github.com/studio-b12/gowebdav/blob/master/file.go) [netrc.go](https://github.com/studio-b12/gowebdav/blob/master/netrc.go) [requests.go](https://github.com/studio-b12/gowebdav/blob/master/requests.go) [utils.go](https://github.com/studio-b12/gowebdav/blob/master/utils.go)
+[auth.go](https://github.com/studio-b12/gowebdav/blob/master/auth.go) [basicAuth.go](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go) [client.go](https://github.com/studio-b12/gowebdav/blob/master/client.go) [digestAuth.go](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go) [doc.go](https://github.com/studio-b12/gowebdav/blob/master/doc.go) [errors.go](https://github.com/studio-b12/gowebdav/blob/master/errors.go) [file.go](https://github.com/studio-b12/gowebdav/blob/master/file.go) [netrc.go](https://github.com/studio-b12/gowebdav/blob/master/netrc.go) [passportAuth.go](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go) [requests.go](https://github.com/studio-b12/gowebdav/blob/master/requests.go) [utils.go](https://github.com/studio-b12/gowebdav/blob/master/utils.go)
+
+### Constants
+``` go
+const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect"
+```
+
+### Variables
+``` go
+var ErrAuthChanged = errors.New("authentication failed, change algorithm")
+```
+ErrAuthChanged must be returned from the Verify method as an error
+to trigger a re-authentication / negotiation with a new authenticator.
+
+``` go
+var ErrTooManyRedirects = errors.New("stopped after 10 redirects")
+```
+ErrTooManyRedirects will be used as return error if a request exceeds 10 redirects.
### func [FixSlash](https://github.com/studio-b12/gowebdav/blob/master/utils.go?s=354:384#L23)
``` go
@@ -237,7 +284,7 @@ func FixSlashes(s string) string
```
FixSlashes appends and prepends a / if they are missing
-### func [IsErrCode](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=355:395#L21)
+### func [IsErrCode](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=740:780#L29)
``` go
func IsErrCode(err error, code int) bool
```
@@ -245,7 +292,7 @@ IsErrCode returns true if the given error
is an os.PathError wrapping a StatusError
with the given status code.
-### func [IsErrNotFound](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=587:621#L31)
+### func [IsErrNotFound](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=972:1006#L39)
``` go
func IsErrNotFound(err error) bool
```
@@ -258,6 +305,16 @@ func Join(path0 string, path1 string) string
```
Join joins two paths
+### func [NewPathError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=1040:1103#L43)
+``` go
+func NewPathError(op string, path string, statusCode int) error
+```
+
+### func [NewPathErrorErr](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=1194:1255#L51)
+``` go
+func NewPathErrorErr(op string, path string, err error) error
+```
+
### func [PathEscape](https://github.com/studio-b12/gowebdav/blob/master/utils.go?s=153:188#L14)
``` go
func PathEscape(path string) string
@@ -277,18 +334,119 @@ func String(r io.Reader) string
```
String pulls a string out of our io.Reader
-### type [Authenticator](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=388:507#L29)
+### type [AuthFactory](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=150:251#L13)
+``` go
+type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error)
+```
+AuthFactory prototype function to create a new Authenticator
+
+### type [Authenticator](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=2155:2695#L56)
``` go
type Authenticator interface {
- Type() string
- User() string
- Pass() string
- Authorize(*http.Request, string, string)
+ // Authorizes a request. Usually by adding some authorization headers.
+ Authorize(c *http.Client, rq *http.Request, path string) error
+ // Verifies the response if the authorization was successful.
+ // May trigger some round trips to pass the authentication.
+ // May also trigger a new Authenticator negotiation by returning `ErrAuthChenged`
+ Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
+ // Creates a copy of the underlying Authenticator.
+ Clone() Authenticator
+ io.Closer
}
```
-Authenticator stub
+A Authenticator implements a specific way to authorize requests.
+Each request is bound to a separate Authenticator instance.
-### type [BasicAuth](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=106:157#L9)
+The authentication flow itself is broken down into `Authorize`
+and `Verify` steps. The former method runs before, and the latter
+runs after the `Request` is submitted.
+This makes it easy to encapsulate and control complex
+authentication challenges.
+
+Some authentication flows causing authentication round trips,
+which can be archived by returning the `redo` of the Verify
+method. `True` restarts the authentication process for the
+current action: A new `Request` is spawned, which must be
+authorized, sent, and re-verified again, until the action
+is successfully submitted.
+The preferred way is to handle the authentication ping-pong
+within `Verify`, and then `redo` with fresh credentials.
+
+The result of the `Verify` method can also trigger an
+`Authenticator` change by returning the `ErrAuthChanged`
+as an error. Depending on the `Authorizer` this may trigger
+an `Authenticator` negotiation.
+
+Set the `XInhibitRedirect` header to '1' in the `Authorize`
+method to get control over request redirection.
+Attention! You must handle the incoming request yourself.
+
+To store a shared session state the `Clone` method **must**
+return a new instance, initialized with the shared state.
+
+#### func [NewDigestAuth](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=324:406#L21)
+``` go
+func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error)
+```
+NewDigestAuth creates a new instance of our Digest Authenticator
+
+#### func [NewPassportAuth](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=386:495#L21)
+``` go
+func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error)
+```
+constructor for PassportAuth creates a new PassportAuth object and
+automatically authenticates against the given partnerURL
+
+### type [Authorizer](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=349:764#L17)
+``` go
+type Authorizer interface {
+ // Creates a new Authenticator Shim per request.
+ // It may track request related states and perform payload buffering
+ // for authentication round trips.
+ // The underlying Authenticator will perform the real authentication.
+ NewAuthenticator(body io.Reader) (Authenticator, io.Reader)
+ // Registers a new Authenticator factory to a key.
+ AddAuthenticator(key string, fn AuthFactory)
+}
+```
+Authorizer our Authenticator factory which creates an
+`Authenticator` per action/request.
+
+#### func [NewAutoAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=3789:3845#L109)
+``` go
+func NewAutoAuth(login string, secret string) Authorizer
+```
+NewAutoAuth creates an auto Authenticator factory.
+It negotiates the default authentication method
+based on the order of the registered Authenticators
+and the remotely offered authentication methods.
+First In, First Out.
+
+#### func [NewEmptyAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=4694:4724#L132)
+``` go
+func NewEmptyAuth() Authorizer
+```
+NewEmptyAuth creates an empty Authenticator factory
+The order of adding the Authenticator matters.
+First In, First Out.
+It offers the `NewAutoAuth` features.
+
+#### func [NewPreemptiveAuth](https://github.com/studio-b12/gowebdav/blob/master/auth.go?s=5300:5353#L148)
+``` go
+func NewPreemptiveAuth(auth Authenticator) Authorizer
+```
+NewPreemptiveAuth creates a preemptive Authenticator
+The preemptive authorizer uses the provided Authenticator
+for every request regardless of any `Www-Authenticate` header.
+
+It may only have one authentication method,
+so calling `AddAuthenticator` **will panic**!
+
+Look out!! This offers the skinniest and slickest implementation
+without any synchronisation!!
+Still applicable with `BasicAuth` within go routines.
+
+### type [BasicAuth](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=94:145#L9)
``` go
type BasicAuth struct {
// contains filtered or unexported fields
@@ -297,31 +455,37 @@ type BasicAuth struct {
```
BasicAuth structure holds our credentials
-#### func (\*BasicAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=473:549#L30)
+#### func (\*BasicAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=180:262#L15)
``` go
-func (b *BasicAuth) Authorize(req *http.Request, method string, path string)
+func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error
```
Authorize the current request
-#### func (\*BasicAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=388:421#L25)
+#### func (\*BasicAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=666:707#L34)
``` go
-func (b *BasicAuth) Pass() string
+func (b *BasicAuth) Clone() Authenticator
```
-Pass holds the BasicAuth password
+Clone creates a Copy of itself
-#### func (\*BasicAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=201:234#L15)
+#### func (\*BasicAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=581:614#L29)
``` go
-func (b *BasicAuth) Type() string
+func (b *BasicAuth) Close() error
```
-Type identifies the BasicAuthenticator
+Close cleans up all resources
-#### func (\*BasicAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=297:330#L20)
+#### func (\*BasicAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=778:813#L40)
``` go
-func (b *BasicAuth) User() string
+func (b *BasicAuth) String() string
```
-User holds the BasicAuth username
+String toString
-### type [Client](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=172:364#L18)
+#### func (\*BasicAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/basicAuth.go?s=352:449#L21)
+``` go
+func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
+```
+Verify verifies if the authentication
+
+### type [Client](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=220:388#L19)
``` go
type Client struct {
// contains filtered or unexported fields
@@ -330,55 +494,61 @@ type Client struct {
```
Client defines our structure
-#### func [NewClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1019:1063#L62)
+#### func [NewAuthClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=608:663#L33)
+``` go
+func NewAuthClient(uri string, auth Authorizer) *Client
+```
+NewAuthClient creates a new client instance with a custom Authorizer
+
+#### func [NewClient](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=436:480#L28)
``` go
func NewClient(uri, user, pw string) *Client
```
NewClient creates a new instance of client
-#### func (\*Client) [Connect](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1843:1875#L87)
+#### func (\*Client) [Connect](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1829:1861#L74)
``` go
func (c *Client) Connect() error
```
Connect connects to our dav server
-#### func (\*Client) [Copy](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6818:6886#L323)
+#### func (\*Client) [Copy](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6815:6883#L310)
``` go
func (c *Client) Copy(oldpath, newpath string, overwrite bool) error
```
Copy copies a file from A to B
-#### func (\*Client) [Mkdir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5793:5855#L272)
+#### func (\*Client) [Mkdir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5790:5852#L259)
``` go
func (c *Client) Mkdir(path string, _ os.FileMode) (err error)
```
Mkdir makes a directory
-#### func (\*Client) [MkdirAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6068:6133#L286)
+#### func (\*Client) [MkdirAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6065:6130#L273)
``` go
func (c *Client) MkdirAll(path string, _ os.FileMode) (err error)
```
MkdirAll like mkdir -p, but for webdav
-#### func (\*Client) [Read](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6992:7042#L328)
+#### func (\*Client) [Read](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6989:7039#L315)
``` go
func (c *Client) Read(path string) ([]byte, error)
```
Read reads the contents of a remote file
-#### func (\*Client) [ReadDir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=2869:2929#L130)
+#### func (\*Client) [ReadDir](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=2855:2915#L117)
``` go
func (c *Client) ReadDir(path string) ([]os.FileInfo, error)
```
ReadDir reads the contents of a remote directory
-#### func (\*Client) [ReadStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=7353:7416#L346)
+#### func (\*Client) [ReadStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=7350:7413#L333)
``` go
func (c *Client) ReadStream(path string) (io.ReadCloser, error)
```
ReadStream reads the stream for a given path
-#### func (\*Client) [ReadStreamRange](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=8165:8255#L368)
+#### func (\*Client) [ReadStreamRange](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=8162:8252#L355)
``` go
func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadCloser, error)
```
@@ -391,61 +561,67 @@ If the server does not support partial content requests and returns full content
this function will emulate the behavior by skipping `offset` bytes and limiting the result
to `length`.
-#### func (\*Client) [Remove](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5299:5341#L249)
+#### func (\*Client) [Remove](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5296:5338#L236)
``` go
func (c *Client) Remove(path string) error
```
Remove removes a remote file
-#### func (\*Client) [RemoveAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5407:5452#L254)
+#### func (\*Client) [RemoveAll](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=5404:5449#L241)
``` go
func (c *Client) RemoveAll(path string) error
```
RemoveAll removes remote files
-#### func (\*Client) [Rename](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6652:6722#L318)
+#### func (\*Client) [Rename](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=6649:6719#L305)
``` go
func (c *Client) Rename(oldpath, newpath string, overwrite bool) error
```
Rename moves a file from A to B
-#### func (\*Client) [SetHeader](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1235:1280#L67)
+#### func (\*Client) [SetHeader](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1092:1137#L49)
``` go
func (c *Client) SetHeader(key, value string)
```
SetHeader lets us set arbitrary headers for a given client
-#### func (\*Client) [SetInterceptor](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1387:1469#L72)
+#### func (\*Client) [SetInterceptor](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1244:1326#L54)
``` go
func (c *Client) SetInterceptor(interceptor func(method string, rq *http.Request))
```
SetInterceptor lets us set an arbitrary interceptor for a given client
-#### func (\*Client) [SetTimeout](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1571:1621#L77)
+#### func (\*Client) [SetJar](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1727:1770#L69)
+``` go
+func (c *Client) SetJar(jar http.CookieJar)
+```
+SetJar exposes the ability to set a cookie jar to the client.
+
+#### func (\*Client) [SetTimeout](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1428:1478#L59)
``` go
func (c *Client) SetTimeout(timeout time.Duration)
```
SetTimeout exposes the ability to set a time limit for requests
-#### func (\*Client) [SetTransport](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1714:1772#L82)
+#### func (\*Client) [SetTransport](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=1571:1629#L64)
``` go
func (c *Client) SetTransport(transport http.RoundTripper)
```
SetTransport exposes the ability to define custom transports
-#### func (\*Client) [Stat](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=4255:4310#L197)
+#### func (\*Client) [Stat](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=4241:4296#L184)
``` go
func (c *Client) Stat(path string) (os.FileInfo, error)
```
Stat returns the file stats for a specified path
-#### func (\*Client) [Write](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9260:9335#L402)
+#### func (\*Client) [Write](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9272:9347#L389)
``` go
func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error)
```
Write writes data to a given path
-#### func (\*Client) [WriteStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9759:9845#L432)
+#### func (\*Client) [WriteStream](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=9771:9857#L419)
``` go
func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err error)
```
@@ -460,29 +636,35 @@ type DigestAuth struct {
```
DigestAuth structure holds our credentials
-#### func (\*DigestAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=577:654#L36)
+#### func (\*DigestAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=525:608#L26)
``` go
-func (d *DigestAuth) Authorize(req *http.Request, method string, path string)
+func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error
```
Authorize the current request
-#### func (\*DigestAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=491:525#L31)
+#### func (\*DigestAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1228:1270#L49)
``` go
-func (d *DigestAuth) Pass() string
+func (d *DigestAuth) Clone() Authenticator
```
-Pass holds the DigestAuth password
+Clone creates a copy of itself
-#### func (\*DigestAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=299:333#L21)
+#### func (\*DigestAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1142:1176#L44)
``` go
-func (d *DigestAuth) Type() string
+func (d *DigestAuth) Close() error
```
-Type identifies the DigestAuthenticator
+Close cleans up all resources
-#### func (\*DigestAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=398:432#L26)
+#### func (\*DigestAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=1466:1502#L58)
``` go
-func (d *DigestAuth) User() string
+func (d *DigestAuth) String() string
```
-User holds the DigestAuth username
+String toString
+
+#### func (\*DigestAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/digestAuth.go?s=912:1010#L36)
+``` go
+func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
+```
+Verify checks for authentication issues and may trigger a re-authentication
### type [File](https://github.com/studio-b12/gowebdav/blob/master/file.go?s=93:253#L10)
``` go
@@ -553,40 +735,46 @@ func (f File) Sys() interface{}
```
Sys ????
-### type [NoAuth](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=551:599#L37)
+### type [PassportAuth](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=125:254#L12)
``` go
-type NoAuth struct {
+type PassportAuth struct {
// contains filtered or unexported fields
}
```
-NoAuth structure holds our credentials
+PassportAuth structure holds our credentials
-#### func (\*NoAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=894:967#L58)
+#### func (\*PassportAuth) [Authorize](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=690:775#L32)
``` go
-func (n *NoAuth) Authorize(req *http.Request, method string, path string)
+func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error
```
Authorize the current request
-#### func (\*NoAuth) [Pass](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=812:842#L53)
+#### func (\*PassportAuth) [Clone](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1701:1745#L69)
``` go
-func (n *NoAuth) Pass() string
+func (p *PassportAuth) Clone() Authenticator
```
-Pass returns the current password
+Clone creates a Copy of itself
-#### func (\*NoAuth) [Type](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=638:668#L43)
+#### func (\*PassportAuth) [Close](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1613:1649#L64)
``` go
-func (n *NoAuth) Type() string
+func (p *PassportAuth) Close() error
```
-Type identifies the authenticator
+Close cleans up all resources
-#### func (\*NoAuth) [User](https://github.com/studio-b12/gowebdav/blob/master/client.go?s=724:754#L48)
+#### func (\*PassportAuth) [String](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=2048:2086#L83)
``` go
-func (n *NoAuth) User() string
+func (p *PassportAuth) String() string
```
-User returns the current user
+String toString
-### type [StatusError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=114:153#L10)
+#### func (\*PassportAuth) [Verify](https://github.com/studio-b12/gowebdav/blob/master/passportAuth.go?s=1075:1175#L46)
+``` go
+func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
+```
+Verify verifies if the authentication is good
+
+### type [StatusError](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=499:538#L18)
``` go
type StatusError struct {
Status int
@@ -596,7 +784,7 @@ type StatusError struct {
StatusError implements error and wraps
an erroneous status code.
-#### func (StatusError) [Error](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=155:191#L14)
+#### func (StatusError) [Error](https://github.com/studio-b12/gowebdav/blob/master/errors.go?s=540:576#L22)
``` go
func (se StatusError) Error() string
```
diff --git a/vendor/github.com/studio-b12/gowebdav/auth.go b/vendor/github.com/studio-b12/gowebdav/auth.go
new file mode 100644
index 0000000000..7842bee198
--- /dev/null
+++ b/vendor/github.com/studio-b12/gowebdav/auth.go
@@ -0,0 +1,409 @@
+package gowebdav
+
+import (
+ "bytes"
+ "errors"
+ "io"
+ "net/http"
+ "strings"
+ "sync"
+)
+
+// AuthFactory prototype function to create a new Authenticator
+type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error)
+
+// Authorizer our Authenticator factory which creates an
+// `Authenticator` per action/request.
+type Authorizer interface {
+ // Creates a new Authenticator Shim per request.
+ // It may track request related states and perform payload buffering
+ // for authentication round trips.
+ // The underlying Authenticator will perform the real authentication.
+ NewAuthenticator(body io.Reader) (Authenticator, io.Reader)
+ // Registers a new Authenticator factory to a key.
+ AddAuthenticator(key string, fn AuthFactory)
+}
+
+// A Authenticator implements a specific way to authorize requests.
+// Each request is bound to a separate Authenticator instance.
+//
+// The authentication flow itself is broken down into `Authorize`
+// and `Verify` steps. The former method runs before, and the latter
+// runs after the `Request` is submitted.
+// This makes it easy to encapsulate and control complex
+// authentication challenges.
+//
+// Some authentication flows causing authentication round trips,
+// which can be archived by returning the `redo` of the Verify
+// method. `True` restarts the authentication process for the
+// current action: A new `Request` is spawned, which must be
+// authorized, sent, and re-verified again, until the action
+// is successfully submitted.
+// The preferred way is to handle the authentication ping-pong
+// within `Verify`, and then `redo` with fresh credentials.
+//
+// The result of the `Verify` method can also trigger an
+// `Authenticator` change by returning the `ErrAuthChanged`
+// as an error. Depending on the `Authorizer` this may trigger
+// an `Authenticator` negotiation.
+//
+// Set the `XInhibitRedirect` header to '1' in the `Authorize`
+// method to get control over request redirection.
+// Attention! You must handle the incoming request yourself.
+//
+// To store a shared session state the `Clone` method **must**
+// return a new instance, initialized with the shared state.
+type Authenticator interface {
+ // Authorizes a request. Usually by adding some authorization headers.
+ Authorize(c *http.Client, rq *http.Request, path string) error
+ // Verifies the response if the authorization was successful.
+ // May trigger some round trips to pass the authentication.
+ // May also trigger a new Authenticator negotiation by returning `ErrAuthChenged`
+ Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
+ // Creates a copy of the underlying Authenticator.
+ Clone() Authenticator
+ io.Closer
+}
+
+type authfactory struct {
+ key string
+ create AuthFactory
+}
+
+// authorizer structure holds our Authenticator create functions
+type authorizer struct {
+ factories []authfactory
+ defAuthMux sync.Mutex
+ defAuth Authenticator
+}
+
+// preemptiveAuthorizer structure holds the preemptive Authenticator
+type preemptiveAuthorizer struct {
+ auth Authenticator
+}
+
+// authShim structure that wraps the real Authenticator
+type authShim struct {
+ factory AuthFactory
+ body io.Reader
+ auth Authenticator
+}
+
+// negoAuth structure holds the authenticators that are going to be negotiated
+type negoAuth struct {
+ auths []Authenticator
+ setDefaultAuthenticator func(auth Authenticator)
+}
+
+// nullAuth initializes the whole authentication flow
+type nullAuth struct{}
+
+// noAuth structure to perform no authentication at all
+type noAuth struct{}
+
+// NewAutoAuth creates an auto Authenticator factory.
+// It negotiates the default authentication method
+// based on the order of the registered Authenticators
+// and the remotely offered authentication methods.
+// First In, First Out.
+func NewAutoAuth(login string, secret string) Authorizer {
+ fmap := make([]authfactory, 0)
+ az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}}
+
+ az.AddAuthenticator("basic", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
+ return &BasicAuth{user: login, pw: secret}, nil
+ })
+
+ az.AddAuthenticator("digest", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
+ return NewDigestAuth(login, secret, rs)
+ })
+
+ az.AddAuthenticator("passport1.4", func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
+ return NewPassportAuth(c, login, secret, rs.Request.URL.String(), &rs.Header)
+ })
+
+ return az
+}
+
+// NewEmptyAuth creates an empty Authenticator factory
+// The order of adding the Authenticator matters.
+// First In, First Out.
+// It offers the `NewAutoAuth` features.
+func NewEmptyAuth() Authorizer {
+ fmap := make([]authfactory, 0)
+ az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}}
+ return az
+}
+
+// NewPreemptiveAuth creates a preemptive Authenticator
+// The preemptive authorizer uses the provided Authenticator
+// for every request regardless of any `Www-Authenticate` header.
+//
+// It may only have one authentication method,
+// so calling `AddAuthenticator` **will panic**!
+//
+// Look out!! This offers the skinniest and slickest implementation
+// without any synchronisation!!
+// Still applicable with `BasicAuth` within go routines.
+func NewPreemptiveAuth(auth Authenticator) Authorizer {
+ return &preemptiveAuthorizer{auth: auth}
+}
+
+// NewAuthenticator creates an Authenticator (Shim) per request
+func (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {
+ var retryBuf io.Reader = body
+ if body != nil {
+ // If the authorization fails, we will need to restart reading
+ // from the passed body stream.
+ // When body is seekable, use seek to reset the streams
+ // cursor to the start.
+ // Otherwise, copy the stream into a buffer while uploading
+ // and use the buffers content on retry.
+ if _, ok := retryBuf.(io.Seeker); ok {
+ body = io.NopCloser(body)
+ } else {
+ buff := &bytes.Buffer{}
+ retryBuf = buff
+ body = io.TeeReader(body, buff)
+ }
+ }
+ a.defAuthMux.Lock()
+ defAuth := a.defAuth.Clone()
+ a.defAuthMux.Unlock()
+
+ return &authShim{factory: a.factory, body: retryBuf, auth: defAuth}, body
+}
+
+// AddAuthenticator appends the AuthFactory to our factories.
+// It converts the key to lower case and preserves the order.
+func (a *authorizer) AddAuthenticator(key string, fn AuthFactory) {
+ key = strings.ToLower(key)
+ for _, f := range a.factories {
+ if f.key == key {
+ panic("Authenticator exists: " + key)
+ }
+ }
+ a.factories = append(a.factories, authfactory{key, fn})
+}
+
+// factory picks all valid Authenticators based on Www-Authenticate headers
+func (a *authorizer) factory(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error) {
+ headers := rs.Header.Values("Www-Authenticate")
+ if len(headers) > 0 {
+ auths := make([]Authenticator, 0)
+ for _, f := range a.factories {
+ for _, header := range headers {
+ headerLower := strings.ToLower(header)
+ if strings.Contains(headerLower, f.key) {
+ rs.Header.Set("Www-Authenticate", header)
+ if auth, err = f.create(c, rs, path); err == nil {
+ auths = append(auths, auth)
+ break
+ }
+ }
+ }
+ }
+
+ switch len(auths) {
+ case 0:
+ return nil, NewPathError("NoAuthenticator", path, rs.StatusCode)
+ case 1:
+ auth = auths[0]
+ default:
+ auth = &negoAuth{auths: auths, setDefaultAuthenticator: a.setDefaultAuthenticator}
+ }
+ } else {
+ auth = &noAuth{}
+ }
+
+ a.setDefaultAuthenticator(auth)
+
+ return auth, nil
+}
+
+// setDefaultAuthenticator sets the default Authenticator
+func (a *authorizer) setDefaultAuthenticator(auth Authenticator) {
+ a.defAuthMux.Lock()
+ a.defAuth.Close()
+ a.defAuth = auth
+ a.defAuthMux.Unlock()
+}
+
+// Authorize the current request
+func (s *authShim) Authorize(c *http.Client, rq *http.Request, path string) error {
+ if err := s.auth.Authorize(c, rq, path); err != nil {
+ return err
+ }
+ body := s.body
+ rq.GetBody = func() (io.ReadCloser, error) {
+ if body != nil {
+ if sk, ok := body.(io.Seeker); ok {
+ if _, err := sk.Seek(0, io.SeekStart); err != nil {
+ return nil, err
+ }
+ }
+ return io.NopCloser(body), nil
+ }
+ return nil, nil
+ }
+ return nil
+}
+
+// Verify checks for authentication issues and may trigger a re-authentication.
+// Catches AlgoChangedErr to update the current Authenticator
+func (s *authShim) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ redo, err = s.auth.Verify(c, rs, path)
+ if err != nil && errors.Is(err, ErrAuthChanged) {
+ if auth, aerr := s.factory(c, rs, path); aerr == nil {
+ s.auth.Close()
+ s.auth = auth
+ return true, nil
+ } else {
+ return false, aerr
+ }
+ }
+ return
+}
+
+// Close closes all resources
+func (s *authShim) Close() error {
+ s.auth.Close()
+ s.auth, s.factory = nil, nil
+ if s.body != nil {
+ if closer, ok := s.body.(io.Closer); ok {
+ return closer.Close()
+ }
+ }
+ return nil
+}
+
+// It's not intend to Clone the shim
+// therefore it returns a noAuth instance
+func (s *authShim) Clone() Authenticator {
+ return &noAuth{}
+}
+
+// String toString
+func (s *authShim) String() string {
+ return "AuthShim"
+}
+
+// Authorize authorizes the current request with the top most Authorizer
+func (n *negoAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
+ if len(n.auths) == 0 {
+ return NewPathError("NoAuthenticator", path, 400)
+ }
+ return n.auths[0].Authorize(c, rq, path)
+}
+
+// Verify verifies the authentication and selects the next one based on the result
+func (n *negoAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ if len(n.auths) == 0 {
+ return false, NewPathError("NoAuthenticator", path, 400)
+ }
+ redo, err = n.auths[0].Verify(c, rs, path)
+ if err != nil {
+ if len(n.auths) > 1 {
+ n.auths[0].Close()
+ n.auths = n.auths[1:]
+ return true, nil
+ }
+ } else if redo {
+ return
+ } else {
+ auth := n.auths[0]
+ n.auths = n.auths[1:]
+ n.setDefaultAuthenticator(auth)
+ return
+ }
+
+ return false, NewPathError("NoAuthenticator", path, rs.StatusCode)
+}
+
+// Close will close the underlying authenticators.
+func (n *negoAuth) Close() error {
+ for _, a := range n.auths {
+ a.Close()
+ }
+ n.setDefaultAuthenticator = nil
+ return nil
+}
+
+// Clone clones the underlying authenticators.
+func (n *negoAuth) Clone() Authenticator {
+ auths := make([]Authenticator, len(n.auths))
+ for i, e := range n.auths {
+ auths[i] = e.Clone()
+ }
+ return &negoAuth{auths: auths, setDefaultAuthenticator: n.setDefaultAuthenticator}
+}
+
+func (n *negoAuth) String() string {
+ return "NegoAuth"
+}
+
+// Authorize the current request
+func (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
+ return nil
+}
+
+// Verify checks for authentication issues and may trigger a re-authentication
+func (n *noAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ if "" != rs.Header.Get("Www-Authenticate") {
+ err = ErrAuthChanged
+ }
+ return
+}
+
+// Close closes all resources
+func (n *noAuth) Close() error {
+ return nil
+}
+
+// Clone creates a copy of itself
+func (n *noAuth) Clone() Authenticator {
+ // no copy due to read only access
+ return n
+}
+
+// String toString
+func (n *noAuth) String() string {
+ return "NoAuth"
+}
+
+// Authorize the current request
+func (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
+ rq.Header.Set(XInhibitRedirect, "1")
+ return nil
+}
+
+// Verify checks for authentication issues and may trigger a re-authentication
+func (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ return true, ErrAuthChanged
+}
+
+// Close closes all resources
+func (n *nullAuth) Close() error {
+ return nil
+}
+
+// Clone creates a copy of itself
+func (n *nullAuth) Clone() Authenticator {
+ // no copy due to read only access
+ return n
+}
+
+// String toString
+func (n *nullAuth) String() string {
+ return "NullAuth"
+}
+
+// NewAuthenticator creates an Authenticator (Shim) per request
+func (b *preemptiveAuthorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {
+ return b.auth.Clone(), body
+}
+
+// AddAuthenticator Will PANIC because it may only have a single authentication method
+func (b *preemptiveAuthorizer) AddAuthenticator(key string, fn AuthFactory) {
+ panic("You're funny! A preemptive authorizer may only have a single authentication method")
+}
diff --git a/vendor/github.com/studio-b12/gowebdav/basicAuth.go b/vendor/github.com/studio-b12/gowebdav/basicAuth.go
index bdb86da580..5511a60f6c 100644
--- a/vendor/github.com/studio-b12/gowebdav/basicAuth.go
+++ b/vendor/github.com/studio-b12/gowebdav/basicAuth.go
@@ -1,7 +1,7 @@
package gowebdav
import (
- "encoding/base64"
+ "fmt"
"net/http"
)
@@ -11,24 +11,32 @@ type BasicAuth struct {
pw string
}
-// Type identifies the BasicAuthenticator
-func (b *BasicAuth) Type() string {
- return "BasicAuth"
-}
-
-// User holds the BasicAuth username
-func (b *BasicAuth) User() string {
- return b.user
-}
-
-// Pass holds the BasicAuth password
-func (b *BasicAuth) Pass() string {
- return b.pw
-}
-
// Authorize the current request
-func (b *BasicAuth) Authorize(req *http.Request, method string, path string) {
- a := b.user + ":" + b.pw
- auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(a))
- req.Header.Set("Authorization", auth)
+func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
+ rq.SetBasicAuth(b.user, b.pw)
+ return nil
+}
+
+// Verify verifies if the authentication
+func (b *BasicAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ if rs.StatusCode == 401 {
+ err = NewPathError("Authorize", path, rs.StatusCode)
+ }
+ return
+}
+
+// Close cleans up all resources
+func (b *BasicAuth) Close() error {
+ return nil
+}
+
+// Clone creates a Copy of itself
+func (b *BasicAuth) Clone() Authenticator {
+ // no copy due to read only access
+ return b
+}
+
+// String toString
+func (b *BasicAuth) String() string {
+ return fmt.Sprintf("BasicAuth login: %s", b.user)
}
diff --git a/vendor/github.com/studio-b12/gowebdav/client.go b/vendor/github.com/studio-b12/gowebdav/client.go
index 7ea68d2447..4a4bb1b6ca 100644
--- a/vendor/github.com/studio-b12/gowebdav/client.go
+++ b/vendor/github.com/studio-b12/gowebdav/client.go
@@ -9,58 +9,43 @@ import (
"net/url"
"os"
pathpkg "path"
+ "strconv"
"strings"
- "sync"
"time"
)
+const XInhibitRedirect = "X-Gowebdav-Inhibit-Redirect"
+
+var defaultProps = []string{"displayname", "resourcetype", "getcontentlength", "getcontenttype", "getetag", "getlastmodified"}
+
// Client defines our structure
type Client struct {
root string
headers http.Header
interceptor func(method string, rq *http.Request)
c *http.Client
-
- authMutex sync.Mutex
- auth Authenticator
-}
-
-// Authenticator stub
-type Authenticator interface {
- Type() string
- User() string
- Pass() string
- Authorize(*http.Request, string, string)
-}
-
-// NoAuth structure holds our credentials
-type NoAuth struct {
- user string
- pw string
-}
-
-// Type identifies the authenticator
-func (n *NoAuth) Type() string {
- return "NoAuth"
-}
-
-// User returns the current user
-func (n *NoAuth) User() string {
- return n.user
-}
-
-// Pass returns the current password
-func (n *NoAuth) Pass() string {
- return n.pw
-}
-
-// Authorize the current request
-func (n *NoAuth) Authorize(req *http.Request, method string, path string) {
+ auth Authorizer
}
// NewClient creates a new instance of client
func NewClient(uri, user, pw string) *Client {
- return &Client{FixSlash(uri), make(http.Header), nil, &http.Client{}, sync.Mutex{}, &NoAuth{user, pw}}
+ return NewAuthClient(uri, NewAutoAuth(user, pw))
+}
+
+// NewAuthClient creates a new client instance with a custom Authorizer
+func NewAuthClient(uri string, auth Authorizer) *Client {
+ c := &http.Client{
+ CheckRedirect: func(rq *http.Request, via []*http.Request) error {
+ if len(via) >= 10 {
+ return ErrTooManyRedirects
+ }
+ if via[0].Header.Get(XInhibitRedirect) != "" {
+ return http.ErrUseLastResponse
+ }
+ return nil
+ },
+ }
+ return &Client{root: FixSlash(uri), headers: make(http.Header), interceptor: nil, c: c, auth: auth}
}
// SetHeader lets us set arbitrary headers for a given client
@@ -83,6 +68,11 @@ func (c *Client) SetTransport(transport http.RoundTripper) {
c.c.Transport = transport
}
+// SetJar exposes the ability to set a cookie jar to the client.
+func (c *Client) SetJar(jar http.CookieJar) {
+ c.c.Jar = jar
+}
+
// Connect connects to our dav server
func (c *Client) Connect() error {
rs, err := c.options("/")
@@ -96,29 +86,86 @@ func (c *Client) Connect() error {
}
if rs.StatusCode != 200 {
- return newPathError("Connect", c.root, rs.StatusCode)
+ return NewPathError("Connect", c.root, rs.StatusCode)
}
return nil
}
-type props struct {
- Status string `xml:"DAV: status"`
- Name string `xml:"DAV: prop>displayname,omitempty"`
- Type xml.Name `xml:"DAV: prop>resourcetype>collection,omitempty"`
- Size string `xml:"DAV: prop>getcontentlength,omitempty"`
- ContentType string `xml:"DAV: prop>getcontenttype,omitempty"`
- ETag string `xml:"DAV: prop>getetag,omitempty"`
- Modified string `xml:"DAV: prop>getlastmodified,omitempty"`
+type Props struct {
+ m map[xml.Name]interface{}
+}
+
+func (c *Props) GetString(key xml.Name) string {
+ return fmt.Sprintf("%v", c.m[key])
+}
+
+func (c *Props) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+ c.m = make(map[xml.Name]interface{})
+
+ type flatProp struct {
+ XMLName xml.Name
+ Content string `xml:",innerxml"`
+ }
+ type flatProps struct {
+ Props []flatProp `xml:",any"`
+ }
+ parsedProps := flatProps{}
+ if err := d.DecodeElement(&parsedProps, &start); err != nil {
+ return err
+ }
+ for _, v := range parsedProps.Props {
+ c.m[v.XMLName] = v.Content
+ }
+ return nil
+}
+
+type propstat struct {
+ Status string `xml:"DAV: status"`
+
+ Props Props `xml:"DAV: prop"`
+}
+
+func (p *propstat) Type() string {
+ if p.Props.GetString(xml.Name{Space: "DAV:", Local: "resourcetype"}) == "" {
+ return "file"
+ }
+ return "collection"
+}
+
+func (p *propstat) Name() string {
+ return p.Props.GetString(xml.Name{Space: "DAV:", Local: "displayname"})
+}
+
+func (p *propstat) ContentType() string {
+ return p.Props.GetString(xml.Name{Space: "DAV:", Local: "getcontenttype"})
+}
+
+func (p *propstat) Size() int64 {
+ if n, e := strconv.ParseInt(p.Props.GetString(xml.Name{Space: "DAV:", Local: "getcontentlength"}), 10, 64); e == nil {
+ return n
+ }
+ return 0
+}
+
+func (p *propstat) ETag() string {
+ return p.Props.GetString(xml.Name{Space: "DAV:", Local: "getetag"})
+}
+
+func (p *propstat) Modified() time.Time {
+ if t, e := time.Parse(time.RFC1123, p.Props.GetString(xml.Name{Space: "DAV:", Local: "getlastmodified"})); e == nil {
+ return t
+ }
+ return time.Unix(0, 0)
}
type response struct {
- Href string `xml:"DAV: href"`
- Props []props `xml:"DAV: propstat"`
+ Href string `xml:"DAV: href"`
+ Propstats []propstat `xml:"DAV: propstat"`
}
-func getProps(r *response, status string) *props {
- for _, prop := range r.Props {
+func getPropstat(r *response, status string) *propstat {
+ for _, prop := range r.Propstats {
if strings.Contains(prop.Status, status) {
return &prop
}
@@ -128,7 +175,16 @@ func getProps(r *response, status string) *props {
// ReadDir reads the contents of a remote directory
func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
- path = FixSlashes(path)
+ return c.ReadDirWithProps(path, defaultProps)
+}
+
+// ReadDirWithProps reads the contents of the directory at the given path, along with the specified properties.
+func (c *Client) ReadDirWithProps(path string, props []string) ([]os.FileInfo, error) {
+ propfindprops := ""
+ if len(props) > 0 {
+ propfindprops = ``
+ }
+
files := make([]os.FileInfo, 0)
skipSelf := true
parse := func(resp interface{}) error {
@@ -136,113 +192,76 @@ func (c *Client) ReadDir(path string) ([]os.FileInfo, error) {
if skipSelf {
skipSelf = false
- if p := getProps(r, "200"); p != nil && p.Type.Local == "collection" {
- r.Props = nil
+ if p := getPropstat(r, "200"); p != nil && p.Type() == "collection" {
+ r.Propstats = nil
return nil
}
- return newPathError("ReadDir", path, 405)
+ return NewPathError("ReadDir", path, 405)
}
- if p := getProps(r, "200"); p != nil {
- f := new(File)
+ if p := getPropstat(r, "200"); p != nil {
+ var name string
if ps, err := url.PathUnescape(r.Href); err == nil {
- f.name = pathpkg.Base(ps)
+ name = pathpkg.Base(ps)
} else {
- f.name = p.Name
+ name = p.Name()
}
- f.path = path + f.name
- f.modified = parseModified(&p.Modified)
- f.etag = p.ETag
- f.contentType = p.ContentType
-
- if p.Type.Local == "collection" {
- f.path += "/"
- f.size = 0
- f.isdir = true
- } else {
- f.size = parseInt64(&p.Size)
- f.isdir = false
- }
-
- files = append(files, *f)
+ files = append(files, *newFile(path, name, p))
}
- r.Props = nil
+ r.Propstats = nil
return nil
}
err := c.propfind(path, false,
- `
-
-
-
-
-
-
-
-
- `,
+ ``+propfindprops+``,
&response{},
parse)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
- err = newPathErrorErr("ReadDir", path, err)
+ err = NewPathErrorErr("ReadDir", path, err)
}
}
return files, err
}
-// Stat returns the file stats for a specified path
+// Stat returns the file stats for a specified path with the default properties
func (c *Client) Stat(path string) (os.FileInfo, error) {
+ return c.StatWithProps(path, defaultProps)
+}
+
+// StatWithProps returns the FileInfo for the specified path along with the specified properties.
+func (c *Client) StatWithProps(path string, props []string) (os.FileInfo, error) {
var f *File
parse := func(resp interface{}) error {
r := resp.(*response)
- if p := getProps(r, "200"); p != nil && f == nil {
- f = new(File)
- f.name = p.Name
- f.path = path
- f.etag = p.ETag
- f.contentType = p.ContentType
-
- if p.Type.Local == "collection" {
- if !strings.HasSuffix(f.path, "/") {
- f.path += "/"
- }
- f.size = 0
- f.modified = time.Unix(0, 0)
- f.isdir = true
- } else {
- f.size = parseInt64(&p.Size)
- f.modified = parseModified(&p.Modified)
- f.isdir = false
- }
+ if p := getPropstat(r, "200"); p != nil && f == nil {
+ f = newFile(".", path, p)
}
- r.Props = nil
+ r.Propstats = nil
return nil
}
+ propXML := ""
+ for _, prop := range props {
+ propXML += ""
+ }
+ propXML += ""
+
err := c.propfind(path, true,
- `
-
-
-
-
-
-
-
-
- `,
+ propXML,
&response{},
parse)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
- err = newPathErrorErr("ReadDir", path, err)
+ return nil, NewPathErrorErr("ReadDir", path, err)
}
+ return nil, err
}
- return f, err
+ return *f, err
}
// Remove removes a remote file
@@ -254,7 +273,7 @@ func (c *Client) Remove(path string) error {
func (c *Client) RemoveAll(path string) error {
rs, err := c.req("DELETE", path, nil, nil)
if err != nil {
- return newPathError("Remove", path, 400)
+ return NewPathError("Remove", path, 400)
}
err = rs.Body.Close()
if err != nil {
@@ -265,7 +284,7 @@ func (c *Client) RemoveAll(path string) error {
return nil
}
- return newPathError("Remove", path, rs.StatusCode)
+ return NewPathError("Remove", path, rs.StatusCode)
}
// Mkdir makes a directory
@@ -279,7 +298,7 @@ func (c *Client) Mkdir(path string, _ os.FileMode) (err error) {
return nil
}
- return newPathError("Mkdir", path, status)
+ return NewPathError("Mkdir", path, status)
}
// MkdirAll like mkdir -p, but for webdav
@@ -305,13 +324,13 @@ func (c *Client) MkdirAll(path string, _ os.FileMode) (err error) {
return
}
if status != 201 {
- return newPathError("MkdirAll", sub, status)
+ return NewPathError("MkdirAll", sub, status)
}
}
return nil
}
- return newPathError("MkdirAll", path, status)
+ return NewPathError("MkdirAll", path, status)
}
// Rename moves a file from A to B
@@ -346,7 +365,7 @@ func (c *Client) Read(path string) ([]byte, error) {
func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
rs, err := c.req("GET", path, nil, nil)
if err != nil {
- return nil, newPathErrorErr("ReadStream", path, err)
+ return nil, NewPathErrorErr("ReadStream", path, err)
}
if rs.StatusCode == 200 {
@@ -354,7 +373,7 @@ func (c *Client) ReadStream(path string) (io.ReadCloser, error) {
}
rs.Body.Close()
- return nil, newPathError("ReadStream", path, rs.StatusCode)
+ return nil, NewPathError("ReadStream", path, rs.StatusCode)
}
// ReadStreamRange reads the stream representing a subset of bytes for a given path,
@@ -374,7 +393,7 @@ func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadClos
}
})
if err != nil {
- return nil, newPathErrorErr("ReadStreamRange", path, err)
+ return nil, NewPathErrorErr("ReadStreamRange", path, err)
}
if rs.StatusCode == http.StatusPartialContent {
@@ -387,15 +406,15 @@ func (c *Client) ReadStreamRange(path string, offset, length int64) (io.ReadClos
if rs.StatusCode == 200 {
// discard first 'offset' bytes.
if _, err := io.Copy(io.Discard, io.LimitReader(rs.Body, offset)); err != nil {
- return nil, newPathErrorErr("ReadStreamRange", path, err)
+ return nil, NewPathErrorErr("ReadStreamRange", path, err)
}
// return a io.ReadCloser that is limited to `length` bytes.
- return &limitedReadCloser{rs.Body, int(length)}, nil
+ return &limitedReadCloser{rc: rs.Body, remaining: int(length)}, nil
}
rs.Body.Close()
- return nil, newPathError("ReadStream", path, rs.StatusCode)
+ return nil, NewPathError("ReadStream", path, rs.StatusCode)
}
// Write writes data to a given path
@@ -425,7 +444,7 @@ func (c *Client) Write(path string, data []byte, _ os.FileMode) (err error) {
}
}
- return newPathError("Write", path, s)
+ return NewPathError("Write", path, s)
}
// WriteStream writes a stream
@@ -446,6 +465,6 @@ func (c *Client) WriteStream(path string, stream io.Reader, _ os.FileMode) (err
return nil
default:
- return newPathError("WriteStream", path, s)
+ return NewPathError("WriteStream", path, s)
}
}
diff --git a/vendor/github.com/studio-b12/gowebdav/digestAuth.go b/vendor/github.com/studio-b12/gowebdav/digestAuth.go
index 4a5eb62f2f..1bcc1de3d0 100644
--- a/vendor/github.com/studio-b12/gowebdav/digestAuth.go
+++ b/vendor/github.com/studio-b12/gowebdav/digestAuth.go
@@ -17,28 +17,46 @@ type DigestAuth struct {
digestParts map[string]string
}
-// Type identifies the DigestAuthenticator
-func (d *DigestAuth) Type() string {
- return "DigestAuth"
-}
-
-// User holds the DigestAuth username
-func (d *DigestAuth) User() string {
- return d.user
-}
-
-// Pass holds the DigestAuth password
-func (d *DigestAuth) Pass() string {
- return d.pw
+// NewDigestAuth creates a new instance of our Digest Authenticator
+func NewDigestAuth(login, secret string, rs *http.Response) (Authenticator, error) {
+ return &DigestAuth{user: login, pw: secret, digestParts: digestParts(rs)}, nil
}
// Authorize the current request
-func (d *DigestAuth) Authorize(req *http.Request, method string, path string) {
+func (d *DigestAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
d.digestParts["uri"] = path
- d.digestParts["method"] = method
+ d.digestParts["method"] = rq.Method
d.digestParts["username"] = d.user
d.digestParts["password"] = d.pw
- req.Header.Set("Authorization", getDigestAuthorization(d.digestParts))
+ rq.Header.Set("Authorization", getDigestAuthorization(d.digestParts))
+ return nil
+}
+
+// Verify checks for authentication issues and may trigger a re-authentication
+func (d *DigestAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ if rs.StatusCode == 401 {
+ err = NewPathError("Authorize", path, rs.StatusCode)
+ }
+ return
+}
+
+// Close cleans up all resources
+func (d *DigestAuth) Close() error {
+ return nil
+}
+
+// Clone creates a copy of itself
+func (d *DigestAuth) Clone() Authenticator {
+ parts := make(map[string]string, len(d.digestParts))
+ for k, v := range d.digestParts {
+ parts[k] = v
+ }
+ return &DigestAuth{user: d.user, pw: d.pw, digestParts: parts}
+}
+
+// String toString
+func (d *DigestAuth) String() string {
+ return fmt.Sprintf("DigestAuth login: %s", d.user)
}
func digestParts(resp *http.Response) map[string]string {
diff --git a/vendor/github.com/studio-b12/gowebdav/errors.go b/vendor/github.com/studio-b12/gowebdav/errors.go
index bbf1e929eb..afc1af6f81 100644
--- a/vendor/github.com/studio-b12/gowebdav/errors.go
+++ b/vendor/github.com/studio-b12/gowebdav/errors.go
@@ -1,10 +1,18 @@
package gowebdav
import (
+ "errors"
"fmt"
"os"
)
+// ErrAuthChanged must be returned from the Verify method as an error
+// to trigger a re-authentication / negotiation with a new authenticator.
+var ErrAuthChanged = errors.New("authentication failed, change algorithm")
+
+// ErrTooManyRedirects will be used as return error if a request exceeds 10 redirects.
+var ErrTooManyRedirects = errors.New("stopped after 10 redirects")
+
// StatusError implements error and wraps
// an erroneous status code.
type StatusError struct {
@@ -32,7 +40,7 @@ func IsErrNotFound(err error) bool {
return IsErrCode(err, 404)
}
-func newPathError(op string, path string, statusCode int) error {
+func NewPathError(op string, path string, statusCode int) error {
return &os.PathError{
Op: op,
Path: path,
@@ -40,7 +48,7 @@ func newPathError(op string, path string, statusCode int) error {
}
}
-func newPathErrorErr(op string, path string, err error) error {
+func NewPathErrorErr(op string, path string, err error) error {
return &os.PathError{
Op: op,
Path: path,
diff --git a/vendor/github.com/studio-b12/gowebdav/file.go b/vendor/github.com/studio-b12/gowebdav/file.go
index ae2303fc8e..8c5a091892 100644
--- a/vendor/github.com/studio-b12/gowebdav/file.go
+++ b/vendor/github.com/studio-b12/gowebdav/file.go
@@ -3,6 +3,7 @@ package gowebdav
import (
"fmt"
"os"
+ "path/filepath"
"time"
)
@@ -15,6 +16,31 @@ type File struct {
modified time.Time
etag string
isdir bool
+ props Props
+}
+
+func newFile(path, name string, p *propstat) *File {
+ f := &File{
+ props: p.Props,
+ }
+ path = FixSlashes(path)
+
+ f.name = name
+ f.path = filepath.Clean(filepath.Join(path, f.name))
+ f.modified = p.Modified()
+ f.etag = p.ETag()
+ f.contentType = p.ContentType()
+ f.props = p.Props
+
+ if p.Type() == "collection" {
+ f.path = filepath.Clean(f.path + "/")
+ f.size = 0
+ f.isdir = true
+ } else {
+ f.size = p.Size()
+ f.isdir = false
+ }
+ return f
}
// Path returns the full path of a file
@@ -64,7 +90,7 @@ func (f File) IsDir() bool {
// Sys ????
func (f File) Sys() interface{} {
- return nil
+ return f.props
}
// String lets us see file information
diff --git a/vendor/github.com/studio-b12/gowebdav/passportAuth.go b/vendor/github.com/studio-b12/gowebdav/passportAuth.go
new file mode 100644
index 0000000000..5e55c536ab
--- /dev/null
+++ b/vendor/github.com/studio-b12/gowebdav/passportAuth.go
@@ -0,0 +1,181 @@
+package gowebdav
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+)
+
+// PassportAuth structure holds our credentials
+type PassportAuth struct {
+ user string
+ pw string
+ cookies []http.Cookie
+ inhibitRedirect bool
+}
+
+// constructor for PassportAuth creates a new PassportAuth object and
+// automatically authenticates against the given partnerURL
+func NewPassportAuth(c *http.Client, user, pw, partnerURL string, header *http.Header) (Authenticator, error) {
+ p := &PassportAuth{
+ user: user,
+ pw: pw,
+ inhibitRedirect: true,
+ }
+ err := p.genCookies(c, partnerURL, header)
+ return p, err
+}
+
+// Authorize the current request
+func (p *PassportAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
+ // prevent redirects to detect subsequent authentication requests
+ if p.inhibitRedirect {
+ rq.Header.Set(XInhibitRedirect, "1")
+ } else {
+ p.inhibitRedirect = true
+ }
+ for _, cookie := range p.cookies {
+ rq.AddCookie(&cookie)
+ }
+ return nil
+}
+
+// Verify verifies if the authentication is good
+func (p *PassportAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
+ switch rs.StatusCode {
+ case 301, 302, 307, 308:
+ redo = true
+ if rs.Header.Get("Www-Authenticate") != "" {
+ // re-authentication required as we are redirected to the login page
+ err = p.genCookies(c, rs.Request.URL.String(), &rs.Header)
+ } else {
+ // just a redirect, follow it
+ p.inhibitRedirect = false
+ }
+ case 401:
+ err = NewPathError("Authorize", path, rs.StatusCode)
+ }
+ return
+}
+
+// Close cleans up all resources
+func (p *PassportAuth) Close() error {
+ return nil
+}
+
+// Clone creates a Copy of itself
+func (p *PassportAuth) Clone() Authenticator {
+ // create a copy to allow independent cookie updates
+ clonedCookies := make([]http.Cookie, len(p.cookies))
+ copy(clonedCookies, p.cookies)
+
+ return &PassportAuth{
+ user: p.user,
+ pw: p.pw,
+ cookies: clonedCookies,
+ inhibitRedirect: true,
+ }
+}
+
+// String toString
+func (p *PassportAuth) String() string {
+ return fmt.Sprintf("PassportAuth login: %s", p.user)
+}
+
+func (p *PassportAuth) genCookies(c *http.Client, partnerUrl string, header *http.Header) error {
+ // For more details refer to:
+ // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pass/2c80637d-438c-4d4b-adc5-903170a779f3
+ // Skipping step 1 and 2 as we already have the partner server challenge
+
+ baseAuthenticationServer := header.Get("Location")
+ baseAuthenticationServerURL, err := url.Parse(baseAuthenticationServer)
+ if err != nil {
+ return err
+ }
+
+ // Skipping step 3 and 4 as we already know that we need and have the user's credentials
+ // Step 5 (Sign-in request)
+ authenticationServerUrl := url.URL{
+ Scheme: baseAuthenticationServerURL.Scheme,
+ Host: baseAuthenticationServerURL.Host,
+ Path: "/login2.srf",
+ }
+
+ partnerServerChallenge := strings.Split(header.Get("Www-Authenticate"), " ")[1]
+
+ req := http.Request{
+ Method: "GET",
+ URL: &authenticationServerUrl,
+ Header: http.Header{
+ "Authorization": []string{"Passport1.4 sign-in=" + url.QueryEscape(p.user) + ",pwd=" + url.QueryEscape(p.pw) + ",OrgVerb=GET,OrgUrl=" + partnerUrl + "," + partnerServerChallenge},
+ },
+ }
+
+ rs, err := c.Do(&req)
+ if err != nil {
+ return err
+ }
+ io.Copy(io.Discard, rs.Body)
+ rs.Body.Close()
+ if rs.StatusCode != 200 {
+ return NewPathError("Authorize", "/", rs.StatusCode)
+ }
+
+ // Step 6 (Token Response from Authentication Server)
+ tokenResponseHeader := rs.Header.Get("Authentication-Info")
+ if tokenResponseHeader == "" {
+ return NewPathError("Authorize", "/", 401)
+ }
+ tokenResponseHeaderList := strings.Split(tokenResponseHeader, ",")
+ token := ""
+ for _, tokenResponseHeader := range tokenResponseHeaderList {
+ if strings.HasPrefix(tokenResponseHeader, "from-PP='") {
+ token = tokenResponseHeader
+ break
+ }
+ }
+ if token == "" {
+ return NewPathError("Authorize", "/", 401)
+ }
+
+ // Step 7 (First Authentication Request to Partner Server)
+ origUrl, err := url.Parse(partnerUrl)
+ if err != nil {
+ return err
+ }
+ req = http.Request{
+ Method: "GET",
+ URL: origUrl,
+ Header: http.Header{
+ "Authorization": []string{"Passport1.4 " + token},
+ },
+ }
+
+ rs, err = c.Do(&req)
+ if err != nil {
+ return err
+ }
+ io.Copy(io.Discard, rs.Body)
+ rs.Body.Close()
+ if rs.StatusCode != 200 && rs.StatusCode != 302 {
+ return NewPathError("Authorize", "/", rs.StatusCode)
+ }
+
+ // Step 8 (Set Token Message from Partner Server)
+ cookies := rs.Header.Values("Set-Cookie")
+ p.cookies = make([]http.Cookie, len(cookies))
+ for i, cookie := range cookies {
+ cookieParts := strings.Split(cookie, ";")
+ cookieName := strings.Split(cookieParts[0], "=")[0]
+ cookieValue := strings.Split(cookieParts[0], "=")[1]
+
+ p.cookies[i] = http.Cookie{
+ Name: cookieName,
+ Value: cookieValue,
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/studio-b12/gowebdav/requests.go b/vendor/github.com/studio-b12/gowebdav/requests.go
index 0966d1ab0b..8e362e8664 100644
--- a/vendor/github.com/studio-b12/gowebdav/requests.go
+++ b/vendor/github.com/studio-b12/gowebdav/requests.go
@@ -1,7 +1,6 @@
package gowebdav
import (
- "bytes"
"io"
"log"
"net/http"
@@ -9,83 +8,52 @@ import (
"strings"
)
-func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
+func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (rs *http.Response, err error) {
+ var redo bool
var r *http.Request
- var retryBuf io.Reader
+ var uri = PathEscape(Join(c.root, path))
+ auth, body := c.auth.NewAuthenticator(body)
+ defer auth.Close()
- if body != nil {
- // If the authorization fails, we will need to restart reading
- // from the passed body stream.
- // When body is seekable, use seek to reset the streams
- // cursor to the start.
- // Otherwise, copy the stream into a buffer while uploading
- // and use the buffers content on retry.
- if sk, ok := body.(io.Seeker); ok {
- if _, err = sk.Seek(0, io.SeekStart); err != nil {
- return
+ for { // TODO auth.continue() strategy(true|n times|until)?
+ if r, err = http.NewRequest(method, uri, body); err != nil {
+ return
+ }
+
+ for k, vals := range c.headers {
+ for _, v := range vals {
+ r.Header.Add(k, v)
}
- retryBuf = body
- } else {
- buff := &bytes.Buffer{}
- retryBuf = buff
- body = io.TeeReader(body, buff)
- }
- r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), body)
- } else {
- r, err = http.NewRequest(method, PathEscape(Join(c.root, path)), nil)
- }
-
- if err != nil {
- return nil, err
- }
-
- for k, vals := range c.headers {
- for _, v := range vals {
- r.Header.Add(k, v)
- }
- }
-
- // make sure we read 'c.auth' only once since it will be substituted below
- // and that is unsafe to do when multiple goroutines are running at the same time.
- c.authMutex.Lock()
- auth := c.auth
- c.authMutex.Unlock()
-
- auth.Authorize(r, method, path)
-
- if intercept != nil {
- intercept(r)
- }
-
- if c.interceptor != nil {
- c.interceptor(method, r)
- }
-
- rs, err := c.c.Do(r)
- if err != nil {
- return nil, err
- }
-
- if rs.StatusCode == 401 && auth.Type() == "NoAuth" {
- wwwAuthenticateHeader := strings.ToLower(rs.Header.Get("Www-Authenticate"))
-
- if strings.Index(wwwAuthenticateHeader, "digest") > -1 {
- c.authMutex.Lock()
- c.auth = &DigestAuth{auth.User(), auth.Pass(), digestParts(rs)}
- c.authMutex.Unlock()
- } else if strings.Index(wwwAuthenticateHeader, "basic") > -1 {
- c.authMutex.Lock()
- c.auth = &BasicAuth{auth.User(), auth.Pass()}
- c.authMutex.Unlock()
- } else {
- return rs, newPathError("Authorize", c.root, rs.StatusCode)
}
- // retryBuf will be nil if body was nil initially so no check
- // for body == nil is required here.
- return c.req(method, path, retryBuf, intercept)
- } else if rs.StatusCode == 401 {
- return rs, newPathError("Authorize", c.root, rs.StatusCode)
+ if err = auth.Authorize(c.c, r, path); err != nil {
+ return
+ }
+
+ if intercept != nil {
+ intercept(r)
+ }
+
+ if c.interceptor != nil {
+ c.interceptor(method, r)
+ }
+
+ if rs, err = c.c.Do(r); err != nil {
+ return
+ }
+
+ if redo, err = auth.Verify(c.c, rs, path); err != nil {
+ rs.Body.Close()
+ return nil, err
+ }
+ if redo {
+ rs.Body.Close()
+ if body, err = r.GetBody(); err != nil {
+ return nil, err
+ }
+ continue
+ }
+ break
}
return rs, err
@@ -131,7 +99,7 @@ func (c *Client) propfind(path string, self bool, body string, resp interface{},
defer rs.Body.Close()
if rs.StatusCode != 207 {
- return newPathError("PROPFIND", path, rs.StatusCode)
+ return NewPathError("PROPFIND", path, rs.StatusCode)
}
return parseXML(rs.Body, resp, parse)
@@ -189,7 +157,7 @@ func (c *Client) copymove(method string, oldpath string, newpath string, overwri
return c.copymove(method, oldpath, newpath, overwrite)
}
- return newPathError(method, oldpath, s)
+ return NewPathError(method, oldpath, s)
}
func (c *Client) put(path string, stream io.Reader) (status int, err error) {
diff --git a/vendor/github.com/studio-b12/gowebdav/utils.go b/vendor/github.com/studio-b12/gowebdav/utils.go
index d466160e86..4fa64b142b 100644
--- a/vendor/github.com/studio-b12/gowebdav/utils.go
+++ b/vendor/github.com/studio-b12/gowebdav/utils.go
@@ -5,9 +5,7 @@ import (
"encoding/xml"
"io"
"net/url"
- "strconv"
"strings"
- "time"
)
// PathEscape escapes all segments of a given path
@@ -49,27 +47,6 @@ func String(r io.Reader) string {
return buf.String()
}
-func parseUint(s *string) uint {
- if n, e := strconv.ParseUint(*s, 10, 32); e == nil {
- return uint(n)
- }
- return 0
-}
-
-func parseInt64(s *string) int64 {
- if n, e := strconv.ParseInt(*s, 10, 64); e == nil {
- return n
- }
- return 0
-}
-
-func parseModified(s *string) time.Time {
- if t, e := time.Parse(time.RFC1123, *s); e == nil {
- return t
- }
- return time.Unix(0, 0)
-}
-
func parseXML(data io.Reader, resp interface{}, parse func(resp interface{}) error) error {
decoder := xml.NewDecoder(data)
for t, _ := decoder.Token(); t != nil; t, _ = decoder.Token() {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 0910d0f218..84d58d0619 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1721,7 +1721,7 @@ github.com/stretchr/objx
github.com/stretchr/testify/assert
github.com/stretchr/testify/mock
github.com/stretchr/testify/require
-# github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423
+# github.com/studio-b12/gowebdav v0.0.0-20221015232716-17255f2e7423 => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159
## explicit; go 1.17
github.com/studio-b12/gowebdav
# github.com/tchap/go-patricia/v2 v2.3.1
@@ -2299,3 +2299,4 @@ stash.kopano.io/kgol/oidc-go
stash.kopano.io/kgol/rndm
# github.com/go-micro/plugins/v4/store/nats-js => github.com/kobergj/plugins/v4/store/nats-js v1.2.1-0.20231020092801-9463c820c19a
# github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20231207143248-4d424e3ae348
+# github.com/studio-b12/gowebdav => github.com/aduffeck/gowebdav v0.0.0-20231123085457-ff658b6ea159