This repository has been archived by the owner on Aug 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 180
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #580 from codevulture/keystone_api
Add support for Keystone Trust (v3)
- Loading branch information
Showing
6 changed files
with
291 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
// Package extensions provides information and interaction with the | ||
// different extensions available for the OpenStack Identity v3 service. | ||
package extensions |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package trust | ||
|
||
import ( | ||
"github.com/rackspace/gophercloud" | ||
"github.com/rackspace/gophercloud/openstack" | ||
token3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens" | ||
) | ||
|
||
type AuthOptionsExt struct { | ||
token3.AuthOptions | ||
TrustID string | ||
} | ||
|
||
func (ao AuthOptionsExt) ToAuthOptionsV3Map(c *gophercloud.ServiceClient, scope *token3.Scope) (map[string]interface{}, error) { | ||
//Passing scope value to nil to add scope later in this function. | ||
authMap, err := ao.AuthOptions.ToAuthOptionsV3Map(c, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Add a "scope" element if a Scope has been provided. | ||
if ao.TrustID != "" { | ||
// TrustID provided. | ||
authMap["scope"] = map[string]interface{}{ | ||
"OS-TRUST:trust": map[string]interface{}{ | ||
"id" : ao.TrustID, | ||
}, | ||
} | ||
} else { | ||
return nil, token3.ErrScopeEmpty | ||
} | ||
return authMap, nil | ||
} | ||
|
||
// AuthenticateV3 explicitly authenticates against the identity v3 service. | ||
func AuthenticateV3Trust(client *gophercloud.ProviderClient, options AuthOptionsExt) error { | ||
return trustv3auth(client, "", options) | ||
} | ||
|
||
func trustv3auth(client *gophercloud.ProviderClient, endpoint string, options AuthOptionsExt) error { | ||
//In case of Trust TokenId would be Provided so we have to populate the value in service client | ||
//to not throw password error,also if it is not provided it will be empty which maintains | ||
//the current implementation. | ||
client.TokenID = options.AuthOptions.TokenID | ||
// Override the generated service endpoint with the one returned by the version endpoint. | ||
v3Client := openstack.NewIdentityV3(client) | ||
if endpoint != "" { | ||
v3Client.Endpoint = endpoint | ||
} | ||
|
||
// copy the auth options to a local variable that we can change. `options` | ||
// needs to stay as-is for reauth purposes | ||
v3Options := options | ||
|
||
var scope *token3.Scope | ||
|
||
result := token3.Create(v3Client, v3Options, scope) | ||
|
||
token, err := result.ExtractToken() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
catalog, err := result.ExtractServiceCatalog() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
client.TokenID = token.ID | ||
|
||
if options.AuthOptions.AllowReauth { | ||
client.ReauthFunc = func() error { | ||
client.TokenID = "" | ||
return trustv3auth(client, endpoint, options) | ||
} | ||
} | ||
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) { | ||
return openstack.V3EndpointURL(catalog, opts) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package trust | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/rackspace/gophercloud" | ||
"github.com/rackspace/gophercloud/openstack/identity/v3/tokens" | ||
"github.com/rackspace/gophercloud/testhelper" | ||
) | ||
|
||
// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure. | ||
func authTokenPost(t *testing.T, options gophercloud.AuthOptions, scope *tokens.Scope, requestJSON string) { | ||
testhelper.SetupHTTP() | ||
defer testhelper.TeardownHTTP() | ||
|
||
client := gophercloud.ServiceClient{ | ||
ProviderClient: &gophercloud.ProviderClient{ | ||
TokenID: "12345abcdef", | ||
}, | ||
Endpoint: testhelper.Endpoint(), | ||
} | ||
|
||
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) { | ||
testhelper.TestMethod(t, r, "POST") | ||
testhelper.TestHeader(t, r, "Content-Type", "application/json") | ||
testhelper.TestHeader(t, r, "Accept", "application/json") | ||
testhelper.TestJSONRequest(t, r, requestJSON) | ||
|
||
w.WriteHeader(http.StatusCreated) | ||
fmt.Fprintf(w, `{ | ||
"token": { | ||
"expires_at": "2014-10-02T13:45:00.000000Z" | ||
} | ||
}`) | ||
}) | ||
|
||
_, err := tokens.Create(&client, AuthOptionsExt{AuthOptions: tokens.AuthOptions{options}, TrustID: "123456"}, scope).Extract() | ||
if err != nil { | ||
t.Errorf("Create returned an error: %v", err) | ||
} | ||
} | ||
|
||
func authTokenPostErr(t *testing.T, options gophercloud.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) { | ||
testhelper.SetupHTTP() | ||
defer testhelper.TeardownHTTP() | ||
|
||
client := gophercloud.ServiceClient{ | ||
ProviderClient: &gophercloud.ProviderClient{}, | ||
Endpoint: testhelper.Endpoint(), | ||
} | ||
if includeToken { | ||
client.TokenID = "abcdef123456" | ||
} | ||
|
||
_, err := tokens.Create(&client, AuthOptionsExt{AuthOptions: tokens.AuthOptions{options}, TrustID: "123456"}, scope).Extract() | ||
if err == nil { | ||
t.Errorf("Create did NOT return an error") | ||
} | ||
if err != expectedErr { | ||
t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err) | ||
} | ||
} | ||
|
||
func TestTrustIDTokenID(t *testing.T) { | ||
options := gophercloud.AuthOptions{TokenID: "old_trustee"} | ||
var scope *tokens.Scope | ||
authTokenPost(t, options, scope, ` | ||
{ | ||
"auth": { | ||
"identity": { | ||
"methods": [ | ||
"token" | ||
], | ||
"token": { | ||
"id": "12345abcdef" | ||
} | ||
}, | ||
"scope": { | ||
"OS-TRUST:trust": { | ||
"id": "123456" | ||
} | ||
} | ||
} | ||
} | ||
`) | ||
} | ||
|
||
func TestFailurePassword(t *testing.T) { | ||
options := gophercloud.AuthOptions{TokenID: "fakeidnopass"} | ||
//Service Client must have tokenId or password, | ||
//setting include tokenId to false | ||
//scope := &Scope{TrustID: "notenough"} | ||
var scope *tokens.Scope | ||
authTokenPostErr(t, options, scope, false, tokens.ErrMissingPassword) | ||
} |
Oops, something went wrong.