Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion agent/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ require (
github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0
github.com/studio-b12/gowebdav v0.9.0
github.com/subosito/gotenv v1.6.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.54
github.com/tomasen/fcgi_client v0.0.0-20180423082037-2bb3d819fd19
Expand Down
2 changes: 0 additions & 2 deletions agent/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -881,8 +881,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/studio-b12/gowebdav v0.9.0 h1:1j1sc9gQnNxbXXM4M/CebPOX4aXYtr7MojAVcN4dHjU=
github.com/studio-b12/gowebdav v0.9.0/go.mod h1:bHA7t77X/QFExdeAnDzK6vKM34kEZAcE1OX4MfiwjkE=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
Expand Down
306 changes: 306 additions & 0 deletions agent/utils/cloud_storage/client/helper/webdav/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,306 @@
package webdav

import (
"bytes"
"errors"
"io"
"net/http"
"strings"
"sync"
)

type AuthFactory func(c *http.Client, rs *http.Response, path string) (auth Authenticator, err error)

type Authorizer interface {
NewAuthenticator(body io.Reader) (Authenticator, io.Reader)
AddAuthenticator(key string, fn AuthFactory)
}

type Authenticator interface {
Authorize(c *http.Client, rq *http.Request, path string) error
Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error)
Clone() Authenticator
io.Closer
}

type authfactory struct {
key string
create AuthFactory
}

type authorizer struct {
factories []authfactory
defAuthMux sync.Mutex
defAuth Authenticator
}

type preemptiveAuthorizer struct {
auth Authenticator
}

type authShim struct {
factory AuthFactory
body io.Reader
auth Authenticator
}

type negoAuth struct {
auths []Authenticator
setDefaultAuthenticator func(auth Authenticator)
}

type nullAuth struct{}

type noAuth struct{}

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
}

func NewEmptyAuth() Authorizer {
fmap := make([]authfactory, 0)
az := &authorizer{factories: fmap, defAuthMux: sync.Mutex{}, defAuth: &nullAuth{}}
return az
}

func NewPreemptiveAuth(auth Authenticator) Authorizer {
return &preemptiveAuthorizer{auth: auth}
}

func (a *authorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {
var retryBuf io.Reader = body
if body != nil {
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
}

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})
}

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
}

func (a *authorizer) setDefaultAuthenticator(auth Authenticator) {
a.defAuthMux.Lock()
a.defAuth.Close()
a.defAuth = auth
a.defAuthMux.Unlock()
}

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
}

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
}

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
}

func (s *authShim) Clone() Authenticator {
return &noAuth{}
}

func (s *authShim) String() string {
return "AuthShim"
}

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)
}

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)
}

func (n *negoAuth) Close() error {
for _, a := range n.auths {
a.Close()
}
n.setDefaultAuthenticator = nil
return nil
}

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"
}

func (n *noAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
return nil
}

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
}

func (n *noAuth) Close() error {
return nil
}

func (n *noAuth) Clone() Authenticator {
return n
}

func (n *noAuth) String() string {
return "NoAuth"
}

func (n *nullAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
rq.Header.Set(XInhibitRedirect, "1")
return nil
}

func (n *nullAuth) Verify(c *http.Client, rs *http.Response, path string) (redo bool, err error) {
return true, ErrAuthChanged
}

func (n *nullAuth) Close() error {
return nil
}

func (n *nullAuth) Clone() Authenticator {
return n
}

func (n *nullAuth) String() string {
return "NullAuth"
}

func (b *preemptiveAuthorizer) NewAuthenticator(body io.Reader) (Authenticator, io.Reader) {
return b.auth.Clone(), body
}

func (b *preemptiveAuthorizer) AddAuthenticator(key string, fn AuthFactory) {
panic("You're funny! A preemptive authorizer may only have a single authentication method")
}
36 changes: 36 additions & 0 deletions agent/utils/cloud_storage/client/helper/webdav/auth_basic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package webdav

import (
"fmt"
"net/http"
)

type BasicAuth struct {
user string
pw string
}

func (b *BasicAuth) Authorize(c *http.Client, rq *http.Request, path string) error {
rq.SetBasicAuth(b.user, b.pw)
return nil
}

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
}

func (b *BasicAuth) Close() error {
return nil
}

func (b *BasicAuth) Clone() Authenticator {
// no copy due to read only access
return b
}

func (b *BasicAuth) String() string {
return fmt.Sprintf("BasicAuth login: %s", b.user)
}
Loading
Loading