blob: ff609aad2f371b8ddbdfc0a0f6a8dc3263ec3bdb [file] [log] [blame]
Samuel A. Falvo II1d3fa662013-06-25 15:29:32 -07001package gophercloud
2
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -07003import (
John Hoppere2bc7022014-06-14 11:30:20 -05004 "fmt"
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -07005 "github.com/racker/perigee"
6)
7
Samuel A. Falvo II4e895182013-06-26 15:44:18 -07008// AuthOptions lets anyone calling Authenticate() supply the required access credentials.
9// At present, only Identity V2 API support exists; therefore, only Username, Password,
10// and optionally, TenantId are provided. If future Identity API versions become available,
11// alternative fields unique to those versions may appear here.
Samuel A. Falvo II1d3fa662013-06-25 15:29:32 -070012type AuthOptions struct {
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070013 // Username and Password are required if using Identity V2 API.
14 // Consult with your provider's control panel to discover your
15 // account's username and password.
16 Username, Password string
17
Rafael Garciae4a550e2013-12-06 17:00:32 -030018 // ApiKey used for providers that support Api Key authentication
19 ApiKey string
20
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070021 // The TenantId field is optional for the Identity V2 API.
22 TenantId string
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -070023
Justin Santa Barbara21682a42013-08-31 17:56:13 -070024 // The TenantName can be specified instead of the TenantId
25 TenantName string
26
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -070027 // AllowReauth should be set to true if you grant permission for Gophercloud to cache
28 // your credentials in memory, and to allow Gophercloud to attempt to re-authenticate
29 // automatically if/when your token expires. If you set it to false, it will not cache
30 // these settings, but re-authentication will not be possible. This setting defaults
31 // to false.
32 AllowReauth bool
Samuel A. Falvo II1d3fa662013-06-25 15:29:32 -070033}
34
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070035// AuthContainer provides a JSON encoding wrapper for passing credentials to the Identity
36// service. You will not work with this structure directly.
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070037type AuthContainer struct {
38 Auth Auth `json:"auth"`
39}
40
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070041// Auth provides a JSON encoding wrapper for passing credentials to the Identity
42// service. You will not work with this structure directly.
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070043type Auth struct {
Rafael Garciae4a550e2013-12-06 17:00:32 -030044 PasswordCredentials *PasswordCredentials `json:"passwordCredentials,omitempty"`
45 ApiKeyCredentials *ApiKeyCredentials `json:"RAX-KSKEY:apiKeyCredentials,omitempty"`
Rafael Garcia752cb332013-12-12 22:16:58 -030046 TenantId string `json:"tenantId,omitempty"`
47 TenantName string `json:"tenantName,omitempty"`
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070048}
49
Samuel A. Falvo II4e895182013-06-26 15:44:18 -070050// PasswordCredentials provides a JSON encoding wrapper for passing credentials to the Identity
51// service. You will not work with this structure directly.
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070052type PasswordCredentials struct {
53 Username string `json:"username"`
54 Password string `json:"password"`
55}
56
Rafael Garciae4a550e2013-12-06 17:00:32 -030057type ApiKeyCredentials struct {
58 Username string `json:"username"`
Rafael Garcia752cb332013-12-12 22:16:58 -030059 ApiKey string `json:"apiKey"`
Rafael Garciae4a550e2013-12-06 17:00:32 -030060}
61
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -070062// Access encapsulates the API token and its relevant fields, as well as the
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -070063// services catalog that Identity API returns once authenticated.
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -070064type Access struct {
65 Token Token
66 ServiceCatalog []CatalogEntry
67 User User
Samuel A. Falvo II20f1aa42013-07-31 14:32:03 -070068 provider Provider `json:"-"`
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -070069 options AuthOptions `json:"-"`
Samuel A. Falvo II20f1aa42013-07-31 14:32:03 -070070 context *Context `json:"-"`
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -070071}
72
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -070073// Token encapsulates an authentication token and when it expires. It also includes
74// tenant information if available.
75type Token struct {
76 Id, Expires string
77 Tenant Tenant
78}
79
80// Tenant encapsulates tenant authentication information. If, after authentication,
81// no tenant information is supplied, both Id and Name will be "".
82type Tenant struct {
83 Id, Name string
84}
85
86// User encapsulates the user credentials, and provides visibility in what
87// the user can do through its role assignments.
88type User struct {
89 Id, Name string
90 XRaxDefaultRegion string `json:"RAX-AUTH:defaultRegion"`
91 Roles []Role
92}
93
94// Role encapsulates a permission that a user can rely on.
95type Role struct {
96 Description, Id, Name string
97}
98
99// CatalogEntry encapsulates a service catalog record.
100type CatalogEntry struct {
101 Name, Type string
102 Endpoints []EntryEndpoint
103}
104
105// EntryEndpoint encapsulates how to get to the API of some service.
106type EntryEndpoint struct {
107 Region, TenantId string
108 PublicURL, InternalURL string
109 VersionId, VersionInfo, VersionList string
110}
111
John Hoppere2bc7022014-06-14 11:30:20 -0500112type AuthError struct {
113 StatusCode int
114}
115
116func (ae *AuthError) Error() string {
117 switch ae.StatusCode {
118 case 401:
119 return "Auth failed. Bad credentials."
120
121 default:
122 return fmt.Sprintf("Auth failed. Status code is: %s.", ae.StatusCode)
123 }
124}
125
Rafael Garciae4a550e2013-12-06 17:00:32 -0300126//
127func getAuthCredentials(options AuthOptions) Auth {
Rafael Garcia752cb332013-12-12 22:16:58 -0300128 if options.ApiKey == "" {
Rafael Garciae4a550e2013-12-06 17:00:32 -0300129 return Auth{
130 PasswordCredentials: &PasswordCredentials{
131 Username: options.Username,
132 Password: options.Password,
133 },
134 TenantId: options.TenantId,
135 TenantName: options.TenantName,
Rafael Garcia752cb332013-12-12 22:16:58 -0300136 }
Rafael Garciae4a550e2013-12-06 17:00:32 -0300137 } else {
138 return Auth{
139 ApiKeyCredentials: &ApiKeyCredentials{
140 Username: options.Username,
Rafael Garcia752cb332013-12-12 22:16:58 -0300141 ApiKey: options.ApiKey,
Rafael Garciae4a550e2013-12-06 17:00:32 -0300142 },
143 TenantId: options.TenantId,
144 TenantName: options.TenantName,
Rafael Garcia752cb332013-12-12 22:16:58 -0300145 }
Rafael Garciae4a550e2013-12-06 17:00:32 -0300146 }
147}
148
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -0700149// papersPlease contains the common logic between authentication and re-authentication.
150// The name, obviously a joke on the process of authentication, was chosen because
151// of how many other entities exist in the program containing the word Auth or Authorization.
152// I didn't need another one.
153func (c *Context) papersPlease(p Provider, options AuthOptions) (*Access, error) {
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -0700154 var access *Access
Matt Martz3927d842014-06-04 10:30:35 -0500155 access = new(Access)
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -0700156
Rafael Garciae4a550e2013-12-06 17:00:32 -0300157 if (options.Username == "") || (options.Password == "" && options.ApiKey == "") {
Samuel A. Falvo IIfd78c302013-06-25 16:35:32 -0700158 return nil, ErrCredentials
159 }
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700160
John Hoppere2bc7022014-06-14 11:30:20 -0500161 resp, err := perigee.Request("POST", p.AuthEndpoint, perigee.Options{
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700162 CustomClient: c.httpClient,
Samuel A. Falvo II839428e2013-06-25 18:02:24 -0700163 ReqBody: &AuthContainer{
Rafael Garciae4a550e2013-12-06 17:00:32 -0300164 Auth: getAuthCredentials(options),
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700165 },
Samuel A. Falvo II4e895182013-06-26 15:44:18 -0700166 Results: &struct {
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -0700167 Access **Access `json:"access"`
168 }{
169 &access,
170 },
Samuel A. Falvo II5d0d74c2013-06-25 17:23:18 -0700171 })
John Hoppere2bc7022014-06-14 11:30:20 -0500172
Samuel A. Falvo II0167aaa2013-07-16 12:36:25 -0700173 if err == nil {
John Hoppere2bc7022014-06-14 11:30:20 -0500174 switch resp.StatusCode {
175 case 200:
176 access.options = options
177 access.provider = p
178 access.context = c
179
180 default:
181 err = &AuthError {
182 StatusCode: resp.StatusCode,
183 }
184 }
Samuel A. Falvo II0167aaa2013-07-16 12:36:25 -0700185 }
John Hoppere2bc7022014-06-14 11:30:20 -0500186
Samuel A. Falvo IId1ee7982013-06-26 14:32:45 -0700187 return access, err
Samuel A. Falvo II1d3fa662013-06-25 15:29:32 -0700188}
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700189
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -0700190// Authenticate() grants access to the OpenStack-compatible provider API.
191//
192// Providers are identified through a unique key string.
193// See the RegisterProvider() method for more details.
194//
195// The supplied AuthOptions instance allows the client to specify only those credentials
196// relevant for the authentication request. At present, support exists for OpenStack
197// Identity V2 API only; support for V3 will become available as soon as documentation for it
198// becomes readily available.
199//
200// For Identity V2 API requirements, you must provide at least the Username and Password
201// options. The TenantId field is optional, and defaults to "".
202func (c *Context) Authenticate(provider string, options AuthOptions) (*Access, error) {
203 p, err := c.ProviderByName(provider)
204 if err != nil {
205 return nil, err
206 }
207 return c.papersPlease(p, options)
208}
209
210// Reauthenticate attempts to reauthenticate using the configured access credentials, if
211// allowed. This method takes no action unless your AuthOptions has the AllowReauth flag
212// set to true.
213func (a *Access) Reauthenticate() error {
214 var other *Access
215 var err error
216
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -0700217 if a.options.AllowReauth {
218 other, err = a.context.papersPlease(a.provider, a.options)
Samuel A. Falvo IIfb586692013-07-16 17:00:14 -0700219 if err == nil {
Samuel A. Falvo II9e64f6b2013-07-16 14:26:50 -0700220 *a = *other
221 }
222 }
223 return err
224}
225
Samuel A. Falvo II2e2b8772013-07-04 15:40:15 -0700226// See AccessProvider interface definition for details.
227func (a *Access) FirstEndpointUrlByCriteria(ac ApiCriteria) string {
228 ep := FindFirstEndpointByCriteria(a.ServiceCatalog, ac)
229 urls := []string{ep.PublicURL, ep.InternalURL}
230 return urls[ac.UrlChoice]
231}
Samuel A. Falvo IIbc0d54a2013-07-08 14:45:21 -0700232
233// See AccessProvider interface definition for details.
234func (a *Access) AuthToken() string {
235 return a.Token.Id
236}
Samuel A. Falvo II659e14b2013-07-16 12:04:54 -0700237
238// See AccessProvider interface definition for details.
239func (a *Access) Revoke(tok string) error {
Samuel A. Falvo II0167aaa2013-07-16 12:36:25 -0700240 url := a.provider.AuthEndpoint + "/" + tok
241 err := perigee.Delete(url, perigee.Options{
242 MoreHeaders: map[string]string{
243 "X-Auth-Token": a.AuthToken(),
244 },
245 OkCodes: []int{204},
246 })
247 return err
Samuel A. Falvo II659e14b2013-07-16 12:04:54 -0700248}
Samuel A. Falvo IIcfb352a2013-11-19 14:39:37 -0800249
250// See ServiceCatalogerForIdentityV2 interface definition for details.
251// Note that the raw slice is returend; be careful not to alter the fields of any members,
252// for other components of Gophercloud may depend upon them.
253// If this becomes a problem in the future,
254// a future revision may return a deep-copy of the service catalog instead.
255func (a *Access) V2ServiceCatalog() []CatalogEntry {
256 return a.ServiceCatalog
257}