Identity v3 Authentication With TrustID (#24)

* delete auth_results

* v3 auth with trust

* define auth errors in gophercloud pkg

* AuthOptionsBuilder interface

* combine error files in gophercloud pkg
diff --git a/openstack/identity/v3/tokens/requests.go b/openstack/identity/v3/tokens/requests.go
index 856c363..b65de02 100644
--- a/openstack/identity/v3/tokens/requests.go
+++ b/openstack/identity/v3/tokens/requests.go
@@ -14,7 +14,9 @@
 type AuthOptionsBuilder interface {
 	// ToTokenV3CreateMap assembles the Create request body, returning an error if parameters are
 	// missing or inconsistent.
-	ToTokenV3CreateMap(*Scope) (map[string]interface{}, error)
+	ToTokenV3CreateMap(map[string]interface{}) (map[string]interface{}, error)
+	ToTokenV3ScopeMap() (map[string]interface{}, error)
+	CanReauth() bool
 }
 
 type AuthOptions struct {
@@ -37,13 +39,6 @@
 	DomainID   string `json:"id,omitempty"`
 	DomainName string `json:"name,omitempty"`
 
-	// The TenantID and TenantName fields are optional for the Identity V2 API.
-	// Some providers allow you to specify a TenantName instead of the TenantId.
-	// Some require both. Your provider's authentication policies will determine
-	// how these fields influence authentication.
-	TenantID   string `json:"tenantId,omitempty"`
-	TenantName string `json:"tenantName,omitempty"`
-
 	// 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
@@ -53,223 +48,91 @@
 
 	// TokenID allows users to authenticate (possibly as another user) with an
 	// authentication token ID.
-	TokenID string
+	TokenID string `json:"-"`
+
+	Scope Scope `json:"-"`
 }
 
-func (opts AuthOptions) ToTokenV3CreateMap(scope *Scope) (map[string]interface{}, error) {
-	type domainReq struct {
-		ID   *string `json:"id,omitempty"`
-		Name *string `json:"name,omitempty"`
+func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
+	gophercloudAuthOpts := gophercloud.AuthOptions{
+		Username:    opts.Username,
+		UserID:      opts.UserID,
+		Password:    opts.Password,
+		DomainID:    opts.DomainID,
+		DomainName:  opts.DomainName,
+		AllowReauth: opts.AllowReauth,
+		TokenID:     opts.TokenID,
 	}
 
-	type projectReq struct {
-		Domain *domainReq `json:"domain,omitempty"`
-		Name   *string    `json:"name,omitempty"`
-		ID     *string    `json:"id,omitempty"`
-	}
+	return gophercloudAuthOpts.ToTokenV3CreateMap(scope)
+}
 
-	type userReq struct {
-		ID       *string    `json:"id,omitempty"`
-		Name     *string    `json:"name,omitempty"`
-		Password string     `json:"password"`
-		Domain   *domainReq `json:"domain,omitempty"`
-	}
-
-	type passwordReq struct {
-		User userReq `json:"user"`
-	}
-
-	type tokenReq struct {
-		ID string `json:"id"`
-	}
-
-	type identityReq struct {
-		Methods  []string     `json:"methods"`
-		Password *passwordReq `json:"password,omitempty"`
-		Token    *tokenReq    `json:"token,omitempty"`
-	}
-
-	type scopeReq struct {
-		Domain  *domainReq  `json:"domain,omitempty"`
-		Project *projectReq `json:"project,omitempty"`
-	}
-
-	type authReq struct {
-		Identity identityReq `json:"identity"`
-		Scope    *scopeReq   `json:"scope,omitempty"`
-	}
-
-	type request struct {
-		Auth authReq `json:"auth"`
-	}
-
-	// Populate the request structure based on the provided arguments. Create and return an error
-	// if insufficient or incompatible information is present.
-	var req request
-
-	// Test first for unrecognized arguments.
-	if opts.TenantID != "" {
-		return nil, ErrTenantIDProvided{}
-	}
-	if opts.TenantName != "" {
-		return nil, ErrTenantNameProvided{}
-	}
-
-	if opts.Password == "" {
-		if opts.TokenID != "" {
-			// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
-			// parameters.
-			if opts.Username != "" {
-				return nil, ErrUsernameWithToken{}
-			}
-			if opts.UserID != "" {
-				return nil, ErrUserIDWithToken{}
-			}
-			if opts.DomainID != "" {
-				return nil, ErrDomainIDWithToken{}
-			}
-			if opts.DomainName != "" {
-				return nil, ErrDomainNameWithToken{}
-			}
-
-			// Configure the request for Token authentication.
-			req.Auth.Identity.Methods = []string{"token"}
-			req.Auth.Identity.Token = &tokenReq{
-				ID: opts.TokenID,
-			}
-		} else {
-			// If no password or token ID are available, authentication can't continue.
-			return nil, ErrMissingPassword{}
+func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
+	if opts.Scope.ProjectName != "" {
+		// ProjectName provided: either DomainID or DomainName must also be supplied.
+		// ProjectID may not be supplied.
+		if opts.Scope.DomainID == "" && opts.Scope.DomainName == "" {
+			return nil, gophercloud.ErrScopeDomainIDOrDomainName{}
 		}
-	} else {
-		// Password authentication.
-		req.Auth.Identity.Methods = []string{"password"}
-
-		// At least one of Username and UserID must be specified.
-		if opts.Username == "" && opts.UserID == "" {
-			return nil, ErrUsernameOrUserID{}
+		if opts.Scope.ProjectID != "" {
+			return nil, gophercloud.ErrScopeProjectIDOrProjectName{}
 		}
 
-		if opts.Username != "" {
-			// If Username is provided, UserID may not be provided.
-			if opts.UserID != "" {
-				return nil, ErrUsernameOrUserID{}
-			}
-
-			// Either DomainID or DomainName must also be specified.
-			if opts.DomainID == "" && opts.DomainName == "" {
-				return nil, ErrDomainIDOrDomainName{}
-			}
-
-			if opts.DomainID != "" {
-				if opts.DomainName != "" {
-					return nil, ErrDomainIDOrDomainName{}
-				}
-
-				// Configure the request for Username and Password authentication with a DomainID.
-				req.Auth.Identity.Password = &passwordReq{
-					User: userReq{
-						Name:     &opts.Username,
-						Password: opts.Password,
-						Domain:   &domainReq{ID: &opts.DomainID},
-					},
-				}
-			}
-
-			if opts.DomainName != "" {
-				// Configure the request for Username and Password authentication with a DomainName.
-				req.Auth.Identity.Password = &passwordReq{
-					User: userReq{
-						Name:     &opts.Username,
-						Password: opts.Password,
-						Domain:   &domainReq{Name: &opts.DomainName},
-					},
-				}
-			}
+		if opts.Scope.DomainID != "" {
+			// ProjectName + DomainID
+			return map[string]interface{}{
+				"project": map[string]interface{}{
+					"name":   &opts.Scope.ProjectName,
+					"domain": map[string]interface{}{"id": &opts.Scope.DomainID},
+				},
+			}, nil
 		}
 
-		if opts.UserID != "" {
-			// If UserID is specified, neither DomainID nor DomainName may be.
-			if opts.DomainID != "" {
-				return nil, ErrDomainIDWithUserID{}
-			}
-			if opts.DomainName != "" {
-				return nil, ErrDomainNameWithUserID{}
-			}
-
-			// Configure the request for UserID and Password authentication.
-			req.Auth.Identity.Password = &passwordReq{
-				User: userReq{ID: &opts.UserID, Password: opts.Password},
-			}
+		if opts.Scope.DomainName != "" {
+			// ProjectName + DomainName
+			return map[string]interface{}{
+				"project": map[string]interface{}{
+					"name":   &opts.Scope.ProjectName,
+					"domain": map[string]interface{}{"name": &opts.Scope.DomainName},
+				},
+			}, nil
 		}
-	}
-
-	// Add a "scope" element if a Scope has been provided.
-	if scope != nil {
-		if scope.ProjectName != "" {
-			// ProjectName provided: either DomainID or DomainName must also be supplied.
-			// ProjectID may not be supplied.
-			if scope.DomainID == "" && scope.DomainName == "" {
-				return nil, ErrScopeDomainIDOrDomainName{}
-			}
-			if scope.ProjectID != "" {
-				return nil, ErrScopeProjectIDOrProjectName{}
-			}
-
-			if scope.DomainID != "" {
-				// ProjectName + DomainID
-				req.Auth.Scope = &scopeReq{
-					Project: &projectReq{
-						Name:   &scope.ProjectName,
-						Domain: &domainReq{ID: &scope.DomainID},
-					},
-				}
-			}
-
-			if scope.DomainName != "" {
-				// ProjectName + DomainName
-				req.Auth.Scope = &scopeReq{
-					Project: &projectReq{
-						Name:   &scope.ProjectName,
-						Domain: &domainReq{Name: &scope.DomainName},
-					},
-				}
-			}
-		} else if scope.ProjectID != "" {
-			// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
-			if scope.DomainID != "" {
-				return nil, ErrScopeProjectIDAlone{}
-			}
-			if scope.DomainName != "" {
-				return nil, ErrScopeProjectIDAlone{}
-			}
-
-			// ProjectID
-			req.Auth.Scope = &scopeReq{
-				Project: &projectReq{ID: &scope.ProjectID},
-			}
-		} else if scope.DomainID != "" {
-			// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
-			if scope.DomainName != "" {
-				return nil, ErrScopeDomainIDOrDomainName{}
-			}
-
-			// DomainID
-			req.Auth.Scope = &scopeReq{
-				Domain: &domainReq{ID: &scope.DomainID},
-			}
-		} else if scope.DomainName != "" {
-			return nil, ErrScopeDomainName{}
-		} else {
-			return nil, ErrScopeEmpty{}
+	} else if opts.Scope.ProjectID != "" {
+		// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
+		if opts.Scope.DomainID != "" {
+			return nil, gophercloud.ErrScopeProjectIDAlone{}
 		}
+		if opts.Scope.DomainName != "" {
+			return nil, gophercloud.ErrScopeProjectIDAlone{}
+		}
+
+		// ProjectID
+		return map[string]interface{}{
+			"project": map[string]interface{}{
+				"id": &opts.Scope.ProjectID,
+			},
+		}, nil
+	} else if opts.Scope.DomainID != "" {
+		// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
+		if opts.Scope.DomainName != "" {
+			return nil, gophercloud.ErrScopeDomainIDOrDomainName{}
+		}
+
+		// DomainID
+		return map[string]interface{}{
+			"domain": map[string]interface{}{
+				"id": &opts.Scope.DomainID,
+			},
+		}, nil
+	} else if opts.Scope.DomainName != "" {
+		return nil, gophercloud.ErrScopeDomainName{}
 	}
 
-	b, err2 := gophercloud.BuildRequestBody(req, "")
-	if err2 != nil {
-		return nil, err2
-	}
-	return b, nil
+	return nil, nil
+}
+
+func (opts *AuthOptions) CanReauth() bool {
+	return opts.AllowReauth
 }
 
 func subjectTokenHeaders(c *gophercloud.ServiceClient, subjectToken string) map[string]string {
@@ -279,12 +142,19 @@
 }
 
 // Create authenticates and either generates a new token, or changes the Scope of an existing token.
-func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder, scopeOpts *Scope) (r CreateResult) {
-	b, err := opts.ToTokenV3CreateMap(scopeOpts)
+func Create(c *gophercloud.ServiceClient, opts AuthOptionsBuilder) (r CreateResult) {
+	scope, err := opts.ToTokenV3ScopeMap()
 	if err != nil {
 		r.Err = err
 		return
 	}
+
+	b, err := opts.ToTokenV3CreateMap(scope)
+	if err != nil {
+		r.Err = err
+		return
+	}
+
 	resp, err := c.Post(tokenURL(c), b, &r.Body, &gophercloud.RequestOpts{
 		MoreHeaders: map[string]string{"X-Auth-Token": ""},
 	})