1package swift
2
3import (
4	"bytes"
5	"encoding/json"
6	"net/http"
7	"strings"
8)
9
10const (
11	v3AuthMethodToken        = "token"
12	v3AuthMethodPassword     = "password"
13	v3CatalogTypeObjectStore = "object-store"
14)
15
16// V3 Authentication request
17// http://docs.openstack.org/developer/keystone/api_curl_examples.html
18// http://developer.openstack.org/api-ref-identity-v3.html
19type v3AuthRequest struct {
20	Auth struct {
21		Identity struct {
22			Methods  []string        `json:"methods"`
23			Password *v3AuthPassword `json:"password,omitempty"`
24			Token    *v3AuthToken    `json:"token,omitempty"`
25		} `json:"identity"`
26		Scope *v3Scope `json:"scope,omitempty"`
27	} `json:"auth"`
28}
29
30type v3Scope struct {
31	Project *v3Project `json:"project,omitempty"`
32	Domain  *v3Domain  `json:"domain,omitempty"`
33	Trust   *v3Trust   `json:"OS-TRUST:trust,omitempty"`
34}
35
36type v3Domain struct {
37	Id   string `json:"id,omitempty"`
38	Name string `json:"name,omitempty"`
39}
40
41type v3Project struct {
42	Name   string    `json:"name,omitempty"`
43	Id     string    `json:"id,omitempty"`
44	Domain *v3Domain `json:"domain,omitempty"`
45}
46
47type v3Trust struct {
48	Id string `json:"id"`
49}
50
51type v3User struct {
52	Domain   *v3Domain `json:"domain,omitempty"`
53	Id       string    `json:"id,omitempty"`
54	Name     string    `json:"name,omitempty"`
55	Password string    `json:"password,omitempty"`
56}
57
58type v3AuthToken struct {
59	Id string `json:"id"`
60}
61
62type v3AuthPassword struct {
63	User v3User `json:"user"`
64}
65
66// V3 Authentication response
67type v3AuthResponse struct {
68	Token struct {
69		Expires_At, Issued_At string
70		Methods               []string
71		Roles                 []struct {
72			Id, Name string
73			Links    struct {
74				Self string
75			}
76		}
77
78		Project struct {
79			Domain struct {
80				Id, Name string
81			}
82			Id, Name string
83		}
84
85		Catalog []struct {
86			Id, Namem, Type string
87			Endpoints       []struct {
88				Id, Region_Id, Url, Region string
89				Interface                  EndpointType
90			}
91		}
92
93		User struct {
94			Id, Name string
95			Domain   struct {
96				Id, Name string
97				Links    struct {
98					Self string
99				}
100			}
101		}
102
103		Audit_Ids []string
104	}
105}
106
107type v3Auth struct {
108	Region  string
109	Auth    *v3AuthResponse
110	Headers http.Header
111}
112
113func (auth *v3Auth) Request(c *Connection) (*http.Request, error) {
114	auth.Region = c.Region
115
116	var v3i interface{}
117
118	v3 := v3AuthRequest{}
119
120	if c.UserName == "" && c.UserId == "" {
121		v3.Auth.Identity.Methods = []string{v3AuthMethodToken}
122		v3.Auth.Identity.Token = &v3AuthToken{Id: c.ApiKey}
123	} else {
124		v3.Auth.Identity.Methods = []string{v3AuthMethodPassword}
125		v3.Auth.Identity.Password = &v3AuthPassword{
126			User: v3User{
127				Name:     c.UserName,
128				Id:       c.UserId,
129				Password: c.ApiKey,
130			},
131		}
132
133		var domain *v3Domain
134
135		if c.Domain != "" {
136			domain = &v3Domain{Name: c.Domain}
137		} else if c.DomainId != "" {
138			domain = &v3Domain{Id: c.DomainId}
139		}
140		v3.Auth.Identity.Password.User.Domain = domain
141	}
142
143	if c.TrustId != "" {
144		v3.Auth.Scope = &v3Scope{Trust: &v3Trust{Id: c.TrustId}}
145	} else if c.TenantId != "" || c.Tenant != "" {
146
147		v3.Auth.Scope = &v3Scope{Project: &v3Project{}}
148
149		if c.TenantId != "" {
150			v3.Auth.Scope.Project.Id = c.TenantId
151		} else if c.Tenant != "" {
152			v3.Auth.Scope.Project.Name = c.Tenant
153			switch {
154			case c.TenantDomain != "":
155				v3.Auth.Scope.Project.Domain = &v3Domain{Name: c.TenantDomain}
156			case c.TenantDomainId != "":
157				v3.Auth.Scope.Project.Domain = &v3Domain{Id: c.TenantDomainId}
158			case c.Domain != "":
159				v3.Auth.Scope.Project.Domain = &v3Domain{Name: c.Domain}
160			case c.DomainId != "":
161				v3.Auth.Scope.Project.Domain = &v3Domain{Id: c.DomainId}
162			default:
163				v3.Auth.Scope.Project.Domain = &v3Domain{Name: "Default"}
164			}
165		}
166	}
167
168	v3i = v3
169
170	body, err := json.Marshal(v3i)
171
172	if err != nil {
173		return nil, err
174	}
175
176	url := c.AuthUrl
177	if !strings.HasSuffix(url, "/") {
178		url += "/"
179	}
180	url += "auth/tokens"
181	req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
182	if err != nil {
183		return nil, err
184	}
185	req.Header.Set("Content-Type", "application/json")
186	req.Header.Set("User-Agent", c.UserAgent)
187	return req, nil
188}
189
190func (auth *v3Auth) Response(resp *http.Response) error {
191	auth.Auth = &v3AuthResponse{}
192	auth.Headers = resp.Header
193	err := readJson(resp, auth.Auth)
194	return err
195}
196
197func (auth *v3Auth) endpointUrl(Type string, endpointType EndpointType) string {
198	for _, catalog := range auth.Auth.Token.Catalog {
199		if catalog.Type == Type {
200			for _, endpoint := range catalog.Endpoints {
201				if endpoint.Interface == endpointType && (auth.Region == "" || (auth.Region == endpoint.Region)) {
202					return endpoint.Url
203				}
204			}
205		}
206	}
207	return ""
208}
209
210func (auth *v3Auth) StorageUrl(Internal bool) string {
211	endpointType := EndpointTypePublic
212	if Internal {
213		endpointType = EndpointTypeInternal
214	}
215	return auth.StorageUrlForEndpoint(endpointType)
216}
217
218func (auth *v3Auth) StorageUrlForEndpoint(endpointType EndpointType) string {
219	return auth.endpointUrl("object-store", endpointType)
220}
221
222func (auth *v3Auth) Token() string {
223	return auth.Headers.Get("X-Subject-Token")
224}
225
226func (auth *v3Auth) CdnUrl() string {
227	return ""
228}
229