Convert ListImages to use reauth.
This commit does not work -- acceptance test 08-... is broken.
diff --git a/acceptance/08-reauthentication.go b/acceptance/08-reauthentication.go
index 31c1fe8..10cd73a 100644
--- a/acceptance/08-reauthentication.go
+++ b/acceptance/08-reauthentication.go
@@ -4,6 +4,7 @@
"fmt"
"flag"
"github.com/rackspace/gophercloud"
+ "time"
)
var quiet = flag.Bool("quiet", false, "Quiet mode for acceptance testing. $? non-zero on error though.")
@@ -19,6 +20,7 @@
gophercloud.AuthOptions{
Username: username,
Password: password,
+ AllowReauth: true, // This enables reauthentication.
},
)
if err != nil {
@@ -47,6 +49,10 @@
// Revoke our current authentication token.
auth.Revoke(auth.AuthToken())
+ time.Sleep(15 * time.Second)
+
+ auth.Reauthenticate()
+ fmt.Println("OLD: ", token1, "\nNEW: ", auth.AuthToken())
// Attempt to list images again. This should _succeed_, because we enabled re-authentication.
_, err = servers.ListImages()
diff --git a/authenticate.go b/authenticate.go
index edd447c..f98c4cc 100644
--- a/authenticate.go
+++ b/authenticate.go
@@ -2,6 +2,7 @@
import (
"github.com/racker/perigee"
+ "fmt"
)
// AuthOptions lets anyone calling Authenticate() supply the required access credentials.
@@ -16,6 +17,13 @@
// The TenantId field is optional for the Identity V2 API.
TenantId string
+
+ // AllowReauth should be set to true if you grant permission for Gophercloud to cache
+ // your credentials in memory, and to allow Gophercloud to attempt to re-authenticate
+ // automatically if/when your token expires. If you set it to false, it will not cache
+ // these settings, but re-authentication will not be possible. This setting defaults
+ // to false.
+ AllowReauth bool
}
// AuthContainer provides a JSON encoding wrapper for passing credentials to the Identity
@@ -45,6 +53,8 @@
ServiceCatalog []CatalogEntry
User User
provider Provider `json:"-"`
+ options AuthOptions `json:"-"`
+ context *Context `json:"-"`
}
// Token encapsulates an authentication token and when it expires. It also includes
@@ -86,30 +96,18 @@
VersionId, VersionInfo, VersionList string
}
-// Authenticate() grants access to the OpenStack-compatible provider API.
-//
-// Providers are identified through a unique key string.
-// See the RegisterProvider() method for more details.
-//
-// The supplied AuthOptions instance allows the client to specify only those credentials
-// relevant for the authentication request. At present, support exists for OpenStack
-// Identity V2 API only; support for V3 will become available as soon as documentation for it
-// becomes readily available.
-//
-// For Identity V2 API requirements, you must provide at least the Username and Password
-// options. The TenantId field is optional, and defaults to "".
-func (c *Context) Authenticate(provider string, options AuthOptions) (*Access, error) {
+// papersPlease contains the common logic between authentication and re-authentication.
+// The name, obviously a joke on the process of authentication, was chosen because
+// of how many other entities exist in the program containing the word Auth or Authorization.
+// I didn't need another one.
+func (c *Context) papersPlease(p Provider, options AuthOptions) (*Access, error) {
var access *Access
- p, err := c.ProviderByName(provider)
- if err != nil {
- return nil, err
- }
if (options.Username == "") || (options.Password == "") {
return nil, ErrCredentials
}
- err = perigee.Post(p.AuthEndpoint, perigee.Options{
+ err := perigee.Post(p.AuthEndpoint, perigee.Options{
CustomClient: c.httpClient,
ReqBody: &AuthContainer{
Auth: Auth{
@@ -127,11 +125,51 @@
},
})
if err == nil {
+ access.options = options
access.provider = p
+ access.context = c
}
return access, err
}
+// Authenticate() grants access to the OpenStack-compatible provider API.
+//
+// Providers are identified through a unique key string.
+// See the RegisterProvider() method for more details.
+//
+// The supplied AuthOptions instance allows the client to specify only those credentials
+// relevant for the authentication request. At present, support exists for OpenStack
+// Identity V2 API only; support for V3 will become available as soon as documentation for it
+// becomes readily available.
+//
+// For Identity V2 API requirements, you must provide at least the Username and Password
+// options. The TenantId field is optional, and defaults to "".
+func (c *Context) Authenticate(provider string, options AuthOptions) (*Access, error) {
+ p, err := c.ProviderByName(provider)
+ if err != nil {
+ return nil, err
+ }
+ return c.papersPlease(p, options)
+}
+
+// Reauthenticate attempts to reauthenticate using the configured access credentials, if
+// allowed. This method takes no action unless your AuthOptions has the AllowReauth flag
+// set to true.
+func (a *Access) Reauthenticate() error {
+ var other *Access
+ var err error
+
+ fmt.Printf("**\n %#v\n", a.options)
+ if a.options.AllowReauth {
+ other, err = a.context.papersPlease(a.provider, a.options)
+ if err != nil {
+ fmt.Println("NEW NEW: ", other.AuthToken())
+ *a = *other
+ }
+ }
+ return err
+}
+
// See AccessProvider interface definition for details.
func (a *Access) FirstEndpointUrlByCriteria(ac ApiCriteria) string {
ep := FindFirstEndpointByCriteria(a.ServiceCatalog, ac)
diff --git a/context.go b/context.go
index 2c70ba5..ebad36c 100644
--- a/context.go
+++ b/context.go
@@ -49,6 +49,9 @@
return &Context{
providerMap: make(map[string]Provider),
httpClient: &http.Client{},
+ reauthHandler: func(acc AccessProvider) error {
+ return acc.Reauthenticate()
+ },
}
}
diff --git a/images.go b/images.go
index 9126b93..cc3f6e2 100644
--- a/images.go
+++ b/images.go
@@ -7,13 +7,15 @@
// See the CloudImagesProvider interface for details.
func (gsp *genericServersProvider) ListImages() ([]Image, error) {
var is []Image
- url := gsp.endpoint + "/images"
- err := perigee.Get(url, perigee.Options{
- CustomClient: gsp.context.httpClient,
- Results: &struct{ Images *[]Image }{&is},
- MoreHeaders: map[string]string{
- "X-Auth-Token": gsp.access.AuthToken(),
- },
+ err := gsp.context.WithReauth(gsp.access, func() error {
+ url := gsp.endpoint + "/images"
+ return perigee.Get(url, perigee.Options{
+ CustomClient: gsp.context.httpClient,
+ Results: &struct{ Images *[]Image }{&is},
+ MoreHeaders: map[string]string{
+ "X-Auth-Token": gsp.access.AuthToken(),
+ },
+ })
})
return is, err
}
diff --git a/interfaces.go b/interfaces.go
index ed16afe..b73a118 100644
--- a/interfaces.go
+++ b/interfaces.go
@@ -15,6 +15,10 @@
// Revoke allows you to terminate any program's access to the OpenStack API by token ID.
Revoke(string) error
+
+ // Reauthenticate attempts to acquire a new authentication token, if the feature is enabled by
+ // AuthOptions.AllowReauth.
+ Reauthenticate() error
}
// CloudServersProvider instances encapsulate a Cloud Servers API, should one exist in the service catalog
diff --git a/reauth_test.go b/reauth_test.go
index 62d3e7e..e6ccb17 100644
--- a/reauth_test.go
+++ b/reauth_test.go
@@ -110,6 +110,9 @@
func (my *MyAccess) Revoke(string) error {
return nil
}
+func (my *MyAccess) Reauthenticate() error {
+ return nil
+}
func TestReauthHandlerUsesSameAccessProvider(t *testing.T) {
fakeAccess := &MyAccess{}
diff --git a/servers_test.go b/servers_test.go
index d95fa40..60c71c8 100644
--- a/servers_test.go
+++ b/servers_test.go
@@ -24,6 +24,10 @@
return nil
}
+func (ta *testAccess) Reauthenticate() error {
+ return nil
+}
+
func TestGetServersApi(t *testing.T) {
c := TestContext().UseCustomClient(&http.Client{Transport: newTransport().WithResponse("Hello")})