Swauth Support (#65)

* Swauth Support

Swauth is an internal authentication system for Swift. This commit adds
support for Swauth in a way that still enables the resulting
*gophercloud.ServiceClient to still be able to use the existing ObjectStorage
API calls.

* Swauth updates for consistency

* Marking required fields for Swauth
diff --git a/openstack/objectstorage/v1/swauth/requests.go b/openstack/objectstorage/v1/swauth/requests.go
new file mode 100644
index 0000000..ce4bf9a
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/requests.go
@@ -0,0 +1,70 @@
+package swauth
+
+import "github.com/gophercloud/gophercloud"
+
+// AuthOptsBuilder describes struct types that can be accepted by the Auth call.
+// The AuthOpts struct in this package does.
+type AuthOptsBuilder interface {
+	ToAuthOptsMap() (map[string]string, error)
+}
+
+// AuthOpts specifies an authentication request.
+type AuthOpts struct {
+	// User is an Swauth-based username in username:tenant format.
+	User string `h:"X-Auth-User" required:"true"`
+	// Key is a secret/password to authenticate the User with.
+	Key string `h:"X-Auth-Key" required:"true"`
+}
+
+// ToAuthOptsMap formats an AuthOpts structure into a request body.
+func (opts AuthOpts) ToAuthOptsMap() (map[string]string, error) {
+	return gophercloud.BuildHeaders(opts)
+}
+
+// Auth performs an authentication request for a Swauth-based user.
+func Auth(c *gophercloud.ProviderClient, opts AuthOptsBuilder) (r GetAuthResult) {
+	h := make(map[string]string)
+
+	if opts != nil {
+		headers, err := opts.ToAuthOptsMap()
+		if err != nil {
+			r.Err = err
+			return
+		}
+
+		for k, v := range headers {
+			h[k] = v
+		}
+	}
+
+	resp, err := c.Request("GET", getURL(c), &gophercloud.RequestOpts{
+		MoreHeaders: h,
+		OkCodes:     []int{200},
+	})
+
+	if resp != nil {
+		r.Header = resp.Header
+	}
+
+	r.Err = err
+
+	return r
+}
+
+// NewObjectStorageV1 creates a Swauth-authenticated *gophercloud.ServiceClient
+// client that can issue ObjectStorage-based API calls.
+func NewObjectStorageV1(pc *gophercloud.ProviderClient, authOpts AuthOpts) (*gophercloud.ServiceClient, error) {
+	auth, err := Auth(pc, authOpts).Extract()
+	if err != nil {
+		return nil, err
+	}
+
+	swiftClient := &gophercloud.ServiceClient{
+		ProviderClient: pc,
+		Endpoint:       auth.StorageURL,
+	}
+
+	swiftClient.TokenID = auth.Token
+
+	return swiftClient, nil
+}
diff --git a/openstack/objectstorage/v1/swauth/results.go b/openstack/objectstorage/v1/swauth/results.go
new file mode 100644
index 0000000..294c43c
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/results.go
@@ -0,0 +1,27 @@
+package swauth
+
+import (
+	"github.com/gophercloud/gophercloud"
+)
+
+// GetAuthResult temporarily contains the response from a Swauth
+// authentication call.
+type GetAuthResult struct {
+	gophercloud.HeaderResult
+}
+
+// AuthResult contains the authentication information from a Swauth
+// authentication request.
+type AuthResult struct {
+	Token      string `json:"X-Auth-Token"`
+	StorageURL string `json:"X-Storage-Url"`
+	CDNURL     string `json:"X-CDN-Management-Url"`
+}
+
+// Extract is a method that attempts to interpret any Swauth authentication
+// response as a AuthResult struct.
+func (r GetAuthResult) Extract() (*AuthResult, error) {
+	var s *AuthResult
+	err := r.ExtractInto(&s)
+	return s, err
+}
diff --git a/openstack/objectstorage/v1/swauth/testing/doc.go b/openstack/objectstorage/v1/swauth/testing/doc.go
new file mode 100644
index 0000000..7603f83
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/testing/doc.go
@@ -0,0 +1 @@
+package testing
diff --git a/openstack/objectstorage/v1/swauth/testing/fixtures.go b/openstack/objectstorage/v1/swauth/testing/fixtures.go
new file mode 100644
index 0000000..f7cbc93
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/testing/fixtures.go
@@ -0,0 +1,29 @@
+package testing
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+// AuthResult is the expected result of AuthOutput
+var AuthResult = swauth.AuthResult{
+	Token:      "AUTH_tk6223e6071f8f4299aa334b48015484a1",
+	StorageURL: "http://127.0.0.1:8080/v1/AUTH_test",
+}
+
+// HandleAuthSuccessfully configures the test server to respond to an Auth request.
+func HandleAuthSuccessfully(t *testing.T, authOpts swauth.AuthOpts) {
+	th.Mux.HandleFunc("/auth/v1.0", func(w http.ResponseWriter, r *http.Request) {
+		th.TestMethod(t, r, "GET")
+		th.TestHeader(t, r, "X-Auth-User", authOpts.User)
+		th.TestHeader(t, r, "X-Auth-Key", authOpts.Key)
+
+		w.Header().Add("X-Auth-Token", AuthResult.Token)
+		w.Header().Add("X-Storage-Url", AuthResult.StorageURL)
+		fmt.Fprintf(w, "")
+	})
+}
diff --git a/openstack/objectstorage/v1/swauth/testing/requests_test.go b/openstack/objectstorage/v1/swauth/testing/requests_test.go
new file mode 100644
index 0000000..57b5034
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/testing/requests_test.go
@@ -0,0 +1,27 @@
+package testing
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/openstack"
+	"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/swauth"
+	th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestAuth(t *testing.T) {
+	authOpts := swauth.AuthOpts{
+		User: "test:tester",
+		Key:  "testing",
+	}
+
+	th.SetupHTTP()
+	defer th.TeardownHTTP()
+	HandleAuthSuccessfully(t, authOpts)
+
+	providerClient, err := openstack.NewClient(th.Endpoint())
+	th.AssertNoErr(t, err)
+
+	swiftClient, err := swauth.NewObjectStorageV1(providerClient, authOpts)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, swiftClient.TokenID, AuthResult.Token)
+}
diff --git a/openstack/objectstorage/v1/swauth/urls.go b/openstack/objectstorage/v1/swauth/urls.go
new file mode 100644
index 0000000..a30cabd
--- /dev/null
+++ b/openstack/objectstorage/v1/swauth/urls.go
@@ -0,0 +1,7 @@
+package swauth
+
+import "github.com/gophercloud/gophercloud"
+
+func getURL(c *gophercloud.ProviderClient) string {
+	return c.IdentityBase + "auth/v1.0"
+}