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

Commit 0d85999

Browse files
authored
Merge pull request #580 from codevulture/keystone_api
Add support for Keystone Trust (v3)
2 parents 1f02c2b + 41add94 commit 0d85999

File tree

6 files changed

+291
-101
lines changed

6 files changed

+291
-101
lines changed

openstack/client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func v3auth(client *gophercloud.ProviderClient, endpoint string, options gopherc
179179
}
180180
}
181181

182-
result := tokens3.Create(v3Client, v3Options, scope)
182+
result := tokens3.Create(v3Client, tokens3.AuthOptions{AuthOptions: v3Options}, scope)
183183

184184
token, err := result.ExtractToken()
185185
if err != nil {
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Package extensions provides information and interaction with the
2+
// different extensions available for the OpenStack Identity v3 service.
3+
package extensions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package trust
2+
3+
import (
4+
"github.com/rackspace/gophercloud"
5+
"github.com/rackspace/gophercloud/openstack"
6+
token3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
7+
)
8+
9+
type AuthOptionsExt struct {
10+
token3.AuthOptions
11+
TrustID string
12+
}
13+
14+
func (ao AuthOptionsExt) ToAuthOptionsV3Map(c *gophercloud.ServiceClient, scope *token3.Scope) (map[string]interface{}, error) {
15+
//Passing scope value to nil to add scope later in this function.
16+
authMap, err := ao.AuthOptions.ToAuthOptionsV3Map(c, nil)
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
// Add a "scope" element if a Scope has been provided.
22+
if ao.TrustID != "" {
23+
// TrustID provided.
24+
authMap["scope"] = map[string]interface{}{
25+
"OS-TRUST:trust": map[string]interface{}{
26+
"id" : ao.TrustID,
27+
},
28+
}
29+
} else {
30+
return nil, token3.ErrScopeEmpty
31+
}
32+
return authMap, nil
33+
}
34+
35+
// AuthenticateV3 explicitly authenticates against the identity v3 service.
36+
func AuthenticateV3Trust(client *gophercloud.ProviderClient, options AuthOptionsExt) error {
37+
return trustv3auth(client, "", options)
38+
}
39+
40+
func trustv3auth(client *gophercloud.ProviderClient, endpoint string, options AuthOptionsExt) error {
41+
//In case of Trust TokenId would be Provided so we have to populate the value in service client
42+
//to not throw password error,also if it is not provided it will be empty which maintains
43+
//the current implementation.
44+
client.TokenID = options.AuthOptions.TokenID
45+
// Override the generated service endpoint with the one returned by the version endpoint.
46+
v3Client := openstack.NewIdentityV3(client)
47+
if endpoint != "" {
48+
v3Client.Endpoint = endpoint
49+
}
50+
51+
// copy the auth options to a local variable that we can change. `options`
52+
// needs to stay as-is for reauth purposes
53+
v3Options := options
54+
55+
var scope *token3.Scope
56+
57+
result := token3.Create(v3Client, v3Options, scope)
58+
59+
token, err := result.ExtractToken()
60+
if err != nil {
61+
return err
62+
}
63+
64+
catalog, err := result.ExtractServiceCatalog()
65+
if err != nil {
66+
return err
67+
}
68+
69+
client.TokenID = token.ID
70+
71+
if options.AuthOptions.AllowReauth {
72+
client.ReauthFunc = func() error {
73+
client.TokenID = ""
74+
return trustv3auth(client, endpoint, options)
75+
}
76+
}
77+
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
78+
return openstack.V3EndpointURL(catalog, opts)
79+
}
80+
81+
return nil
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package trust
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/rackspace/gophercloud"
9+
"github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
10+
"github.com/rackspace/gophercloud/testhelper"
11+
)
12+
13+
// authTokenPost verifies that providing certain AuthOptions and Scope results in an expected JSON structure.
14+
func authTokenPost(t *testing.T, options gophercloud.AuthOptions, scope *tokens.Scope, requestJSON string) {
15+
testhelper.SetupHTTP()
16+
defer testhelper.TeardownHTTP()
17+
18+
client := gophercloud.ServiceClient{
19+
ProviderClient: &gophercloud.ProviderClient{
20+
TokenID: "12345abcdef",
21+
},
22+
Endpoint: testhelper.Endpoint(),
23+
}
24+
25+
testhelper.Mux.HandleFunc("/auth/tokens", func(w http.ResponseWriter, r *http.Request) {
26+
testhelper.TestMethod(t, r, "POST")
27+
testhelper.TestHeader(t, r, "Content-Type", "application/json")
28+
testhelper.TestHeader(t, r, "Accept", "application/json")
29+
testhelper.TestJSONRequest(t, r, requestJSON)
30+
31+
w.WriteHeader(http.StatusCreated)
32+
fmt.Fprintf(w, `{
33+
"token": {
34+
"expires_at": "2014-10-02T13:45:00.000000Z"
35+
}
36+
}`)
37+
})
38+
39+
_, err := tokens.Create(&client, AuthOptionsExt{AuthOptions: tokens.AuthOptions{options}, TrustID: "123456"}, scope).Extract()
40+
if err != nil {
41+
t.Errorf("Create returned an error: %v", err)
42+
}
43+
}
44+
45+
func authTokenPostErr(t *testing.T, options gophercloud.AuthOptions, scope *tokens.Scope, includeToken bool, expectedErr error) {
46+
testhelper.SetupHTTP()
47+
defer testhelper.TeardownHTTP()
48+
49+
client := gophercloud.ServiceClient{
50+
ProviderClient: &gophercloud.ProviderClient{},
51+
Endpoint: testhelper.Endpoint(),
52+
}
53+
if includeToken {
54+
client.TokenID = "abcdef123456"
55+
}
56+
57+
_, err := tokens.Create(&client, AuthOptionsExt{AuthOptions: tokens.AuthOptions{options}, TrustID: "123456"}, scope).Extract()
58+
if err == nil {
59+
t.Errorf("Create did NOT return an error")
60+
}
61+
if err != expectedErr {
62+
t.Errorf("Create returned an unexpected error: wanted %v, got %v", expectedErr, err)
63+
}
64+
}
65+
66+
func TestTrustIDTokenID(t *testing.T) {
67+
options := gophercloud.AuthOptions{TokenID: "old_trustee"}
68+
var scope *tokens.Scope
69+
authTokenPost(t, options, scope, `
70+
{
71+
"auth": {
72+
"identity": {
73+
"methods": [
74+
"token"
75+
],
76+
"token": {
77+
"id": "12345abcdef"
78+
}
79+
},
80+
"scope": {
81+
"OS-TRUST:trust": {
82+
"id": "123456"
83+
}
84+
}
85+
}
86+
}
87+
88+
`)
89+
}
90+
91+
func TestFailurePassword(t *testing.T) {
92+
options := gophercloud.AuthOptions{TokenID: "fakeidnopass"}
93+
//Service Client must have tokenId or password,
94+
//setting include tokenId to false
95+
//scope := &Scope{TrustID: "notenough"}
96+
var scope *tokens.Scope
97+
authTokenPostErr(t, options, scope, false, tokens.ErrMissingPassword)
98+
}

0 commit comments

Comments
 (0)