Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

Commit

Permalink
Merge pull request #580 from codevulture/keystone_api
Browse files Browse the repository at this point in the history
Add support for Keystone Trust (v3)
  • Loading branch information
jrperritt authored Aug 3, 2016
2 parents 1f02c2b + 41add94 commit 0d85999
Show file tree
Hide file tree
Showing 6 changed files with 291 additions and 101 deletions.
2 changes: 1 addition & 1 deletion openstack/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
}
}

result := tokens3.Create(v3Client, v3Options, scope)
result := tokens3.Create(v3Client, tokens3.AuthOptions{AuthOptions: v3Options}, scope)

token, err := result.ExtractToken()
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions openstack/identity/v3/extensions/doc.go
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
82 changes: 82 additions & 0 deletions openstack/identity/v3/extensions/trust/request.go
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
}
98 changes: 98 additions & 0 deletions openstack/identity/v3/extensions/trust/request_tests.go
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)
}
Loading

0 comments on commit 0d85999

Please sign in to comment.