blob: 46e55f9c0f1256f8e599a3471f404d225cdcf052 [file] [log] [blame]
Ash Wilson70dfe0c2014-08-28 13:57:09 -04001package gophercloud
2
Ash Wilson730a5062014-10-31 15:13:35 -04003/*
Jon Perrittdb0ae142016-03-13 00:33:41 -06004type AuthOptionsBuilder interface {
5 ToTokenCreateMap() (map[string]interface{}, error)
6}
7*/
8
9/*
Ash Wilson730a5062014-10-31 15:13:35 -040010AuthOptions stores information needed to authenticate to an OpenStack cluster.
11You can populate one manually, or use a provider's AuthOptionsFromEnv() function
12to read relevant information from the standard environment variables. Pass one
13to a provider's AuthenticatedClient function to authenticate and obtain a
14ProviderClient representing an active session on that provider.
15
16Its fields are the union of those recognized by each identity implementation and
17provider.
18*/
Ash Wilson70dfe0c2014-08-28 13:57:09 -040019type AuthOptions struct {
Jamie Hannafordb280dea2014-10-24 15:14:06 +020020 // IdentityEndpoint specifies the HTTP endpoint that is required to work with
Ash Wilson730a5062014-10-31 15:13:35 -040021 // the Identity API of the appropriate version. While it's ultimately needed by
22 // all of the identity services, it will often be populated by a provider-level
23 // function.
Jon Perrittdb0ae142016-03-13 00:33:41 -060024 IdentityEndpoint string `json:"-"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040025
Jamie Hannafordb280dea2014-10-24 15:14:06 +020026 // Username is required if using Identity V2 API. Consult with your provider's
27 // control panel to discover your account's username. In Identity V3, either
Ash Wilson730a5062014-10-31 15:13:35 -040028 // UserID or a combination of Username and DomainID or DomainName are needed.
Jon Perrittdb0ae142016-03-13 00:33:41 -060029 Username string `json:"username,omitempty"`
30 UserID string `json:"id,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040031
Jon Perrittdb0ae142016-03-13 00:33:41 -060032 Password string `json:"password,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040033
Jamie Hannafordb280dea2014-10-24 15:14:06 +020034 // At most one of DomainID and DomainName must be provided if using Username
35 // with Identity V3. Otherwise, either are optional.
Jon Perrittdb0ae142016-03-13 00:33:41 -060036 DomainID string `json:"id,omitempty"`
37 DomainName string `json:"name,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040038
39 // The TenantID and TenantName fields are optional for the Identity V2 API.
40 // Some providers allow you to specify a TenantName instead of the TenantId.
Ash Wilson730a5062014-10-31 15:13:35 -040041 // Some require both. Your provider's authentication policies will determine
Ash Wilson70dfe0c2014-08-28 13:57:09 -040042 // how these fields influence authentication.
Jon Perrittdb0ae142016-03-13 00:33:41 -060043 TenantID string `json:"tenantId,omitempty"`
44 TenantName string `json:"tenantName,omitempty"`
Ash Wilson70dfe0c2014-08-28 13:57:09 -040045
46 // AllowReauth should be set to true if you grant permission for Gophercloud to
47 // cache your credentials in memory, and to allow Gophercloud to attempt to
48 // re-authenticate automatically if/when your token expires. If you set it to
49 // false, it will not cache these settings, but re-authentication will not be
50 // possible. This setting defaults to false.
Jon Perrittdb0ae142016-03-13 00:33:41 -060051 AllowReauth bool `json:"-"`
jrperritt95b74c82015-07-28 20:39:27 -060052
jrperritt1f218c82015-07-29 08:54:18 -060053 // TokenID allows users to authenticate (possibly as another user) with an
54 // authentication token ID.
55 TokenID string
Ash Wilson70dfe0c2014-08-28 13:57:09 -040056}
Jon Perrittdb0ae142016-03-13 00:33:41 -060057
58// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
59// interface in the v2 tokens package
60func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
61 v2Opts := AuthOptionsV2{
62 PasswordCredentials: &PasswordCredentialsV2{
63 Username: opts.Username,
64 Password: opts.Password,
65 },
66 TenantID: opts.TenantID,
67 TenantName: opts.TenantName,
68 TokenCredentials: &TokenCredentialsV2{
69 ID: opts.TokenID,
70 },
71 }
72
73 b, err := BuildRequestBody(v2Opts, "auth")
74 if err != nil {
75 return nil, err
76 }
77 /*
78 if opts.TokenID == "" {
79 delete(b["auth"].(map[string]interface{}), "token")
80 return b, nil
81 }
82
83 delete(b["auth"].(map[string]interface{}), "passwordCredentials")*/
84 return b, nil
85}
86
87func (opts AuthOptions) ToTokenV3CreateMap(scope *ScopeOptsV3) (map[string]interface{}, error) {
Jon Perritt397ade62016-03-15 06:55:02 -050088 /*
89 type domainReq struct {
90 ID *string `json:"id,omitempty"`
91 Name *string `json:"name,omitempty"`
92 }
93
94 type projectReq struct {
95 Domain *domainReq `json:"domain,omitempty"`
96 Name *string `json:"name,omitempty"`
97 ID *string `json:"id,omitempty"`
98 }
99
100 type userReq struct {
101 ID *string `json:"id,omitempty"`
102 Name *string `json:"name,omitempty"`
103 Password string `json:"password"`
104 Domain *domainReq `json:"domain,omitempty"`
105 }
106
107 type passwordReq struct {
108 User userReq `json:"user"`
109 }
110
111 type tokenReq struct {
112 ID string `json:"id"`
113 }
114
115 type identityReq struct {
116 Methods []string `json:"methods"`
117 Password *passwordReq `json:"password,omitempty"`
118 Token *tokenReq `json:"token,omitempty"`
119 }
120
121 type scopeReq struct {
122 Domain *domainReq `json:"domain,omitempty"`
123 Project *projectReq `json:"project,omitempty"`
124 }
125
126 type authReq struct {
127 Identity identityReq `json:"identity"`
128 Scope *scopeReq `json:"scope,omitempty"`
129 }
130
131 type request struct {
132 Auth authReq `json:"auth"`
133 }
134
135 // Populate the request structure based on the provided arguments. Create and return an error
136 // if insufficient or incompatible information is present.
137 var req request
138
139 // Test first for unrecognized arguments.
140 if options.APIKey != "" {
141 return createErr(ErrAPIKeyProvided)
142 }
143 if options.TenantID != "" {
144 return createErr(ErrTenantIDProvided)
145 }
146 if options.TenantName != "" {
147 return createErr(ErrTenantNameProvided)
148 }
149
150 if options.Password == "" {
151 if c.TokenID != "" {
152 // Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
153 // parameters.
154 if options.Username != "" {
155 return createErr(ErrUsernameWithToken)
156 }
157 if options.UserID != "" {
158 return createErr(ErrUserIDWithToken)
159 }
160 if options.DomainID != "" {
161 return createErr(ErrDomainIDWithToken)
162 }
163 if options.DomainName != "" {
164 return createErr(ErrDomainNameWithToken)
165 }
166
167 // Configure the request for Token authentication.
168 req.Auth.Identity.Methods = []string{"token"}
169 req.Auth.Identity.Token = &tokenReq{
170 ID: c.TokenID,
171 }
172 } else {
173 // If no password or token ID are available, authentication can't continue.
174 return createErr(ErrMissingPassword)
175 }
176 } else {
177 // Password authentication.
178 req.Auth.Identity.Methods = []string{"password"}
179
180 // At least one of Username and UserID must be specified.
181 if options.Username == "" && options.UserID == "" {
182 return createErr(ErrUsernameOrUserID)
183 }
184
185 if options.Username != "" {
186 // If Username is provided, UserID may not be provided.
187 if options.UserID != "" {
188 return createErr(ErrUsernameOrUserID)
189 }
190
191 // Either DomainID or DomainName must also be specified.
192 if options.DomainID == "" && options.DomainName == "" {
193 return createErr(ErrDomainIDOrDomainName)
194 }
195
196 if options.DomainID != "" {
197 if options.DomainName != "" {
198 return createErr(ErrDomainIDOrDomainName)
199 }
200
201 // Configure the request for Username and Password authentication with a DomainID.
202 req.Auth.Identity.Password = &passwordReq{
203 User: userReq{
204 Name: &options.Username,
205 Password: options.Password,
206 Domain: &domainReq{ID: &options.DomainID},
207 },
208 }
209 }
210
211 if options.DomainName != "" {
212 // Configure the request for Username and Password authentication with a DomainName.
213 req.Auth.Identity.Password = &passwordReq{
214 User: userReq{
215 Name: &options.Username,
216 Password: options.Password,
217 Domain: &domainReq{Name: &options.DomainName},
218 },
219 }
220 }
221 }
222
223 if options.UserID != "" {
224 // If UserID is specified, neither DomainID nor DomainName may be.
225 if options.DomainID != "" {
226 return createErr(ErrDomainIDWithUserID)
227 }
228 if options.DomainName != "" {
229 return createErr(ErrDomainNameWithUserID)
230 }
231
232 // Configure the request for UserID and Password authentication.
233 req.Auth.Identity.Password = &passwordReq{
234 User: userReq{ID: &options.UserID, Password: options.Password},
235 }
236 }
237 }
238
239 // Add a "scope" element if a Scope has been provided.
240 if scope != nil {
241 if scope.ProjectName != "" {
242 // ProjectName provided: either DomainID or DomainName must also be supplied.
243 // ProjectID may not be supplied.
244 if scope.DomainID == "" && scope.DomainName == "" {
245 return createErr(ErrScopeDomainIDOrDomainName)
246 }
247 if scope.ProjectID != "" {
248 return createErr(ErrScopeProjectIDOrProjectName)
249 }
250
251 if scope.DomainID != "" {
252 // ProjectName + DomainID
253 req.Auth.Scope = &scopeReq{
254 Project: &projectReq{
255 Name: &scope.ProjectName,
256 Domain: &domainReq{ID: &scope.DomainID},
257 },
258 }
259 }
260
261 if scope.DomainName != "" {
262 // ProjectName + DomainName
263 req.Auth.Scope = &scopeReq{
264 Project: &projectReq{
265 Name: &scope.ProjectName,
266 Domain: &domainReq{Name: &scope.DomainName},
267 },
268 }
269 }
270 } else if scope.ProjectID != "" {
271 // ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
272 if scope.DomainID != "" {
273 return createErr(ErrScopeProjectIDAlone)
274 }
275 if scope.DomainName != "" {
276 return createErr(ErrScopeProjectIDAlone)
277 }
278
279 // ProjectID
280 req.Auth.Scope = &scopeReq{
281 Project: &projectReq{ID: &scope.ProjectID},
282 }
283 } else if scope.DomainID != "" {
284 // DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
285 if scope.DomainName != "" {
286 return createErr(ErrScopeDomainIDOrDomainName)
287 }
288
289 // DomainID
290 req.Auth.Scope = &scopeReq{
291 Domain: &domainReq{ID: &scope.DomainID},
292 }
293 } else if scope.DomainName != "" {
294 return createErr(ErrScopeDomainName)
295 } else {
296 return createErr(ErrScopeEmpty)
297 }
298 }
299 */
300
Jon Perrittdb0ae142016-03-13 00:33:41 -0600301 var methods []string
302 if opts.TokenID != "" {
303 methods = []string{"token"}
304 } else {
305 methods = []string{"password"}
306 }
307
308 v3Opts := AuthOptionsV3{
309 Identity: &IdentityCredentialsV3{
310 Methods: methods,
311 PasswordCredentials: &PasswordCredentialsV3{
312 User: &UserV3{
313 ID: opts.UserID,
314 Name: opts.Username,
315 Password: opts.Password,
316 Domain: &DomainV3{
317 ID: opts.DomainID,
318 Name: opts.DomainName,
319 },
320 },
321 },
322 TokenCredentials: &TokenCredentialsV3{
323 ID: opts.TokenID,
324 },
325 },
326 }
327
328 if scope != nil {
329 v3Opts.Scope = &ScopeV3{
330 Domain: &ScopeDomainV3{
331 ID: scope.DomainID,
332 Name: scope.DomainName,
333 },
334 Project: &ScopeProjectV3{
335 Domain: &ScopeProjectDomainV3{
336 ID: scope.DomainID,
337 Name: scope.DomainName,
338 },
339 ID: scope.ProjectID,
340 Name: scope.ProjectName,
341 },
342 }
343 }
344
345 b, err := BuildRequestBody(v3Opts, "auth")
346 if err != nil {
347 return nil, err
348 }
Jon Perrittdb0ae142016-03-13 00:33:41 -0600349
Jon Perrittdb0ae142016-03-13 00:33:41 -0600350 return b, nil
351}