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

Commit c2fa289

Browse files
committed
Merge pull request #451 from jrperritt/token-auth
allow token authentication
2 parents f9a5c84 + 1f218c8 commit c2fa289

File tree

5 files changed

+89
-15
lines changed

5 files changed

+89
-15
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// +build acceptance
2+
3+
package v2
4+
5+
import (
6+
"os"
7+
"testing"
8+
9+
"github.com/rackspace/gophercloud"
10+
"github.com/rackspace/gophercloud/acceptance/tools"
11+
"github.com/rackspace/gophercloud/rackspace"
12+
"github.com/rackspace/gophercloud/rackspace/identity/v2/tokens"
13+
th "github.com/rackspace/gophercloud/testhelper"
14+
)
15+
16+
func rackspaceAuthOptions(t *testing.T) gophercloud.AuthOptions {
17+
// Obtain credentials from the environment.
18+
options, err := rackspace.AuthOptionsFromEnv()
19+
th.AssertNoErr(t, err)
20+
options = tools.OnlyRS(options)
21+
22+
if options.Username == "" {
23+
t.Fatal("Please provide a Rackspace username as RS_USERNAME.")
24+
}
25+
if options.APIKey == "" {
26+
t.Fatal("Please provide a Rackspace API key as RS_API_KEY.")
27+
}
28+
29+
return options
30+
}
31+
32+
func createClient(t *testing.T, auth bool) *gophercloud.ServiceClient {
33+
ao := rackspaceAuthOptions(t)
34+
35+
provider, err := rackspace.NewClient(ao.IdentityEndpoint)
36+
th.AssertNoErr(t, err)
37+
38+
if auth {
39+
err = rackspace.Authenticate(provider, ao)
40+
th.AssertNoErr(t, err)
41+
}
42+
43+
return rackspace.NewIdentityV2(provider)
44+
}
45+
46+
func TestTokenAuth(t *testing.T) {
47+
authedClient := createClient(t, true)
48+
token := authedClient.TokenID
49+
50+
tenantID := os.Getenv("RS_TENANT_ID")
51+
if tenantID == "" {
52+
t.Skip("You must set RS_TENANT_ID environment variable to run this test")
53+
}
54+
55+
authOpts := tokens.AuthOptions{}
56+
authOpts.TenantID = tenantID
57+
authOpts.Token = token
58+
59+
_, err := tokens.Create(authedClient, authOpts).ExtractToken()
60+
th.AssertNoErr(t, err)
61+
}

auth_options.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ type AuthOptions struct {
4343
// false, it will not cache these settings, but re-authentication will not be
4444
// possible. This setting defaults to false.
4545
AllowReauth bool
46+
47+
// TokenID allows users to authenticate (possibly as another user) with an
48+
// authentication token ID.
49+
TokenID string
4650
}

openstack/identity/v2/tokens/errors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var (
1818
// ErrDomainNameProvided is returned if you attempt to authenticate with a DomainName.
1919
ErrDomainNameProvided = unacceptedAttributeErr("DomainName")
2020

21-
// ErrUsernameRequired is returned if you attempt ot authenticate without a Username.
21+
// ErrUsernameRequired is returned if you attempt to authenticate without a Username.
2222
ErrUsernameRequired = errors.New("You must supply a Username in your AuthOptions.")
2323

2424
// ErrPasswordRequired is returned if you don't provide a password.

openstack/identity/v2/tokens/requests.go

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package tokens
22

3-
import "github.com/rackspace/gophercloud"
3+
import (
4+
"fmt"
5+
6+
"github.com/rackspace/gophercloud"
7+
)
48

59
// AuthOptionsBuilder describes any argument that may be passed to the Create call.
610
type AuthOptionsBuilder interface {
@@ -38,20 +42,24 @@ func (auth AuthOptions) ToTokenCreateMap() (map[string]interface{}, error) {
3842
return nil, ErrDomainNameProvided
3943
}
4044

41-
// Username and Password are always required.
42-
if auth.Username == "" {
43-
return nil, ErrUsernameRequired
44-
}
45-
if auth.Password == "" {
46-
return nil, ErrPasswordRequired
47-
}
48-
4945
// Populate the request map.
5046
authMap := make(map[string]interface{})
5147

52-
authMap["passwordCredentials"] = map[string]interface{}{
53-
"username": auth.Username,
54-
"password": auth.Password,
48+
if auth.Username != "" {
49+
if auth.Password != "" {
50+
authMap["passwordCredentials"] = map[string]interface{}{
51+
"username": auth.Username,
52+
"password": auth.Password,
53+
}
54+
} else {
55+
return nil, ErrPasswordRequired
56+
}
57+
} else if auth.TokenID != "" {
58+
authMap["token"] = map[string]interface{}{
59+
"id": auth.TokenID,
60+
}
61+
} else {
62+
return nil, fmt.Errorf("You must provide either username/password or tenantID/token values.")
5563
}
5664

5765
if auth.TenantID != "" {

openstack/identity/v2/tokens/requests_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package tokens
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/rackspace/gophercloud"
@@ -22,7 +23,7 @@ func tokenPostErr(t *testing.T, options gophercloud.AuthOptions, expectedErr err
2223
HandleTokenPost(t, "")
2324

2425
actualErr := Create(client.ServiceClient(), AuthOptions{options}).Err
25-
th.CheckEquals(t, expectedErr, actualErr)
26+
th.CheckDeepEquals(t, expectedErr, actualErr)
2627
}
2728

2829
func TestCreateWithPassword(t *testing.T) {
@@ -128,7 +129,7 @@ func TestRequireUsername(t *testing.T) {
128129
Password: "thing",
129130
}
130131

131-
tokenPostErr(t, options, ErrUsernameRequired)
132+
tokenPostErr(t, options, fmt.Errorf("You must provide either username/password or tenantID/token values."))
132133
}
133134

134135
func TestRequirePassword(t *testing.T) {

0 commit comments

Comments
 (0)