blob: aef7e71b391e6ffbe2d74ee1b861d3db3beb9507 [file] [log] [blame]
Ash Wilson8ba82242014-08-28 15:38:55 -04001package openstack
2
3import (
Ash Wilson4dee1b82014-08-29 14:56:45 -04004 "errors"
Ash Wilsona87ee062014-09-03 11:26:06 -04005 "fmt"
6 "net/url"
Ash Wilsoned6a1d82014-09-03 12:01:00 -04007 "strings"
Ash Wilson4dee1b82014-08-29 14:56:45 -04008
Ash Wilson8ba82242014-08-28 15:38:55 -04009 "github.com/rackspace/gophercloud"
Ash Wilson11c98282014-09-08 16:05:10 -040010 identity2 "github.com/rackspace/gophercloud/openstack/identity/v2"
Ash Wilsona87ee062014-09-03 11:26:06 -040011 tokens3 "github.com/rackspace/gophercloud/openstack/identity/v3/tokens"
Ash Wilson4dee1b82014-08-29 14:56:45 -040012 "github.com/rackspace/gophercloud/openstack/utils"
Ash Wilson8ba82242014-08-28 15:38:55 -040013)
14
Ash Wilson4dee1b82014-08-29 14:56:45 -040015const (
16 v20 = "v2.0"
17 v30 = "v3.0"
18)
Ash Wilson8ba82242014-08-28 15:38:55 -040019
Ash Wilsona87ee062014-09-03 11:26:06 -040020// NewClient prepares an unauthenticated ProviderClient instance.
21// Most users will probably prefer using the AuthenticatedClient function instead.
22// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
23// for example.
24func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
25 // Normalize the identity endpoint that's provided by trimming any path, query or fragment from the URL.
26 u, err := url.Parse(endpoint)
27 if err != nil {
28 return nil, err
29 }
30 u.Path, u.RawQuery, u.Fragment = "", "", ""
31 normalized := u.String()
32
33 return &gophercloud.ProviderClient{
34 IdentityEndpoint: normalized,
35 Reauthenticate: func() error {
36 return errors.New("Unable to reauthenticate before authenticating the first time.")
37 },
38 }, nil
39}
40
41// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
Ash Wilsonccd020b2014-09-02 10:40:54 -040042// returns a Client instance that's ready to operate.
Ash Wilson8ba82242014-08-28 15:38:55 -040043// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
44// the most recent identity service available to proceed.
Ash Wilsona87ee062014-09-03 11:26:06 -040045func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
46 client, err := NewClient(options.IdentityEndpoint)
47 if err != nil {
48 return nil, err
49 }
50
51 err = Authenticate(client, options)
Ash Wilsonccd020b2014-09-02 10:40:54 -040052 if err != nil {
53 return nil, err
54 }
55 return client, nil
56}
57
Ash Wilsonccd020b2014-09-02 10:40:54 -040058// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
Ash Wilsona87ee062014-09-03 11:26:06 -040059func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
Ash Wilson4dee1b82014-08-29 14:56:45 -040060 versions := []*utils.Version{
61 &utils.Version{ID: v20, Priority: 20},
62 &utils.Version{ID: v30, Priority: 30},
63 }
64
Ash Wilsona87ee062014-09-03 11:26:06 -040065 chosen, endpoint, err := utils.ChooseVersion(client.IdentityEndpoint, versions)
Ash Wilson4dee1b82014-08-29 14:56:45 -040066 if err != nil {
Ash Wilsonccd020b2014-09-02 10:40:54 -040067 return err
Ash Wilson4dee1b82014-08-29 14:56:45 -040068 }
69
Ash Wilsoned6a1d82014-09-03 12:01:00 -040070 if !strings.HasSuffix(endpoint, "/") {
71 endpoint = endpoint + "/"
72 }
73
Ash Wilson4dee1b82014-08-29 14:56:45 -040074 switch chosen.ID {
75 case v20:
Ash Wilson11c98282014-09-08 16:05:10 -040076 v2Client := NewIdentityV2(client)
77 v2Client.Endpoint = endpoint
78 fmt.Printf("Endpoint is: %s\n", endpoint)
79
80 result, err := identity2.Authenticate(v2Client, options)
81 if err != nil {
82 return err
83 }
84
85 token, err := identity2.GetToken(result)
86 if err != nil {
87 return err
88 }
89 client.TokenID = token.ID
90
91 return nil
Ash Wilson4dee1b82014-08-29 14:56:45 -040092 case v30:
Ash Wilsona87ee062014-09-03 11:26:06 -040093 // Override the generated service endpoint with the one returned by the version endpoint.
94 v3Client := NewIdentityV3(client)
95 v3Client.Endpoint = endpoint
96
97 result, err := tokens3.Create(v3Client, options, nil)
98 if err != nil {
99 return err
100 }
101
102 client.TokenID, err = result.TokenID()
103 if err != nil {
104 return err
105 }
106
107 return nil
Ash Wilson4dee1b82014-08-29 14:56:45 -0400108 default:
Ash Wilsonccd020b2014-09-02 10:40:54 -0400109 // The switch statement must be out of date from the versions list.
Ash Wilsona87ee062014-09-03 11:26:06 -0400110 return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
Ash Wilsonccd020b2014-09-02 10:40:54 -0400111 }
112}
113
Ash Wilsona87ee062014-09-03 11:26:06 -0400114// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
115func NewIdentityV2(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
Ash Wilsoned6a1d82014-09-03 12:01:00 -0400116 v2Endpoint := client.IdentityEndpoint + "/v2.0/"
Ash Wilsonccd020b2014-09-02 10:40:54 -0400117
Ash Wilsona87ee062014-09-03 11:26:06 -0400118 return &gophercloud.ServiceClient{
119 Provider: client,
120 Endpoint: v2Endpoint,
Ash Wilson4dee1b82014-08-29 14:56:45 -0400121 }
Ash Wilson8ba82242014-08-28 15:38:55 -0400122}
123
Ash Wilsona87ee062014-09-03 11:26:06 -0400124// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
125func NewIdentityV3(client *gophercloud.ProviderClient) *gophercloud.ServiceClient {
Ash Wilsoned6a1d82014-09-03 12:01:00 -0400126 v3Endpoint := client.IdentityEndpoint + "/v3/"
Ash Wilsona87ee062014-09-03 11:26:06 -0400127
128 return &gophercloud.ServiceClient{
129 Provider: client,
130 Endpoint: v3Endpoint,
131 }
Ash Wilson8ba82242014-08-28 15:38:55 -0400132}