Skip to content

Fix races related to the way how authLock is implemented and used #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 6, 2021
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: 1 addition & 0 deletions compatibility_1_0.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Go 1.0 compatibility functions

//go:build !go1.1
// +build !go1.1

package swift
Expand Down
1 change: 1 addition & 0 deletions compatibility_1_1.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Go 1.1 and later compatibility functions
//
//go:build go1.1
// +build go1.1

package swift
Expand Down
1 change: 1 addition & 0 deletions compatibility_1_6.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build go1.6
// +build go1.6

package swift
Expand Down
1 change: 1 addition & 0 deletions compatibility_not_1_6.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build !go1.6
// +build !go1.6

package swift
Expand Down
38 changes: 29 additions & 9 deletions swift.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ type Connection struct {
Expires time.Time // time the token expires, may be Zero if unknown
client *http.Client
Auth Authenticator `json:"-" xml:"-"` // the current authenticator
authLock *sync.Mutex // lock when R/W StorageUrl, AuthToken, Auth
authLock sync.Mutex // lock when R/W StorageUrl, AuthToken, Auth
// swiftInfo is filled after QueryInfo is called
swiftInfo SwiftInfo
}
Expand Down Expand Up @@ -459,9 +459,6 @@ func (c *Connection) setDefaults() {
// If you don't call it before calling one of the connection methods
// then it will be called for you on the first access.
func (c *Connection) Authenticate(ctx context.Context) (err error) {
if c.authLock == nil {
c.authLock = &sync.Mutex{}
}
c.authLock.Lock()
defer c.authLock.Unlock()
return c.authenticate(ctx)
Expand Down Expand Up @@ -584,9 +581,6 @@ func (c *Connection) UnAuthenticate() {
//
// Doesn't actually check the credentials against the server.
func (c *Connection) Authenticated() bool {
if c.authLock == nil {
c.authLock = &sync.Mutex{}
}
c.authLock.Lock()
defer c.authLock.Unlock()
return c.authenticated()
Expand Down Expand Up @@ -631,7 +625,11 @@ func (i SwiftInfo) SLOMinSegmentSize() int64 {

// Discover Swift configuration by doing a request against /info
func (c *Connection) QueryInfo(ctx context.Context) (infos SwiftInfo, err error) {
infoUrl, err := url.Parse(c.StorageUrl)
storageUrl, err := c.GetStorageUrl(ctx)
if err != nil {
return nil, err
}
infoUrl, err := url.Parse(storageUrl)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1864,8 +1862,15 @@ func (c *Connection) ObjectDelete(ctx context.Context, container string, objectN

// ObjectTempUrl returns a temporary URL for an object
func (c *Connection) ObjectTempUrl(container string, objectName string, secretKey string, method string, expires time.Time) string {
c.authLock.Lock()
storageUrl := c.StorageUrl
c.authLock.Unlock()
if storageUrl == "" {
return "" // Cannot do better without changing the interface
}

mac := hmac.New(sha1.New, []byte(secretKey))
prefix, _ := url.Parse(c.StorageUrl)
prefix, _ := url.Parse(storageUrl)
body := fmt.Sprintf("%s\n%d\n%s/%s/%s", method, expires.Unix(), prefix.Path, container, objectName)
mac.Write([]byte(body))
sig := hex.EncodeToString(mac.Sum(nil))
Expand Down Expand Up @@ -2292,3 +2297,18 @@ func (c *Connection) VersionObjectList(ctx context.Context, version, object stri
}
return c.ObjectNames(ctx, version, opts)
}

// GetStorageUrl returns Swift storage URL.
func (c *Connection) GetStorageUrl(ctx context.Context) (string, error) {
c.authLock.Lock()
defer c.authLock.Unlock()

// Return cached URL even if authentication has expired
if c.StorageUrl == "" {
err := c.authenticate(ctx)
if err != nil {
return "", err
}
}
return c.StorageUrl, nil
}