1package oauth1
2
3import (
4	"encoding/json"
5	"net/url"
6	"time"
7
8	"github.com/gophercloud/gophercloud"
9	"github.com/gophercloud/gophercloud/pagination"
10)
11
12// Consumer represents a delegated authorization request between two
13// identities.
14type Consumer struct {
15	ID          string `json:"id"`
16	Secret      string `json:"secret"`
17	Description string `json:"description"`
18}
19
20type consumerResult struct {
21	gophercloud.Result
22}
23
24// CreateConsumerResult is the response from a Create operation. Call its
25// Extract method to interpret it as a Consumer.
26type CreateConsumerResult struct {
27	consumerResult
28}
29
30// UpdateConsumerResult is the response from a Create operation. Call its
31// Extract method to interpret it as a Consumer.
32type UpdateConsumerResult struct {
33	consumerResult
34}
35
36// DeleteConsumerResult is the response from a Delete operation. Call its
37// ExtractErr to determine if the request succeeded or failed.
38type DeleteConsumerResult struct {
39	gophercloud.ErrResult
40}
41
42// ConsumersPage is a single page of Region results.
43type ConsumersPage struct {
44	pagination.LinkedPageBase
45}
46
47// GetConsumerResult is the response from a Get operation. Call its Extract
48// method to interpret it as a Consumer.
49type GetConsumerResult struct {
50	consumerResult
51}
52
53// IsEmpty determines whether or not a page of Consumers contains any results.
54func (c ConsumersPage) IsEmpty() (bool, error) {
55	consumers, err := ExtractConsumers(c)
56	return len(consumers) == 0, err
57}
58
59// NextPageURL extracts the "next" link from the links section of the result.
60func (c ConsumersPage) NextPageURL() (string, error) {
61	var s struct {
62		Links struct {
63			Next     string `json:"next"`
64			Previous string `json:"previous"`
65		} `json:"links"`
66	}
67	err := c.ExtractInto(&s)
68	if err != nil {
69		return "", err
70	}
71	return s.Links.Next, err
72}
73
74// ExtractConsumers returns a slice of Consumers contained in a single page of
75// results.
76func ExtractConsumers(r pagination.Page) ([]Consumer, error) {
77	var s struct {
78		Consumers []Consumer `json:"consumers"`
79	}
80	err := (r.(ConsumersPage)).ExtractInto(&s)
81	return s.Consumers, err
82}
83
84// Extract interprets any consumer result as a Consumer.
85func (c consumerResult) Extract() (*Consumer, error) {
86	var s struct {
87		Consumer *Consumer `json:"consumer"`
88	}
89	err := c.ExtractInto(&s)
90	return s.Consumer, err
91}
92
93// Token contains an OAuth1 token.
94type Token struct {
95	// OAuthToken is the key value for the oauth token that the Identity API returns.
96	OAuthToken string `q:"oauth_token"`
97	// OAuthTokenSecret is the secret value associated with the OAuth Token.
98	OAuthTokenSecret string `q:"oauth_token_secret"`
99	// OAUthExpiresAt is the date and time when an OAuth token expires.
100	OAUthExpiresAt *time.Time `q:"-"`
101}
102
103// TokenResult is a struct to handle
104// "Content-Type: application/x-www-form-urlencoded" response.
105type TokenResult struct {
106	gophercloud.Result
107	Body []byte
108}
109
110// Extract interprets any OAuth1 token result as a Token.
111func (r TokenResult) Extract() (*Token, error) {
112	if r.Err != nil {
113		return nil, r.Err
114	}
115
116	values, err := url.ParseQuery(string(r.Body))
117	if err != nil {
118		return nil, err
119	}
120
121	token := &Token{
122		OAuthToken:       values.Get("oauth_token"),
123		OAuthTokenSecret: values.Get("oauth_token_secret"),
124	}
125
126	if v := values.Get("oauth_expires_at"); v != "" {
127		if t, err := time.Parse(gophercloud.RFC3339Milli, v); err != nil {
128			return nil, err
129		} else {
130			token.OAUthExpiresAt = &t
131		}
132	}
133
134	return token, nil
135}
136
137// AuthorizedToken contains an OAuth1 authorized token info.
138type AuthorizedToken struct {
139	// OAuthVerifier is the ID of the token verifier.
140	OAuthVerifier string `json:"oauth_verifier"`
141}
142
143type AuthorizeTokenResult struct {
144	gophercloud.Result
145}
146
147// Extract interprets AuthorizeTokenResult result as a AuthorizedToken.
148func (r AuthorizeTokenResult) Extract() (*AuthorizedToken, error) {
149	var s struct {
150		AuthorizedToken *AuthorizedToken `json:"token"`
151	}
152	err := r.ExtractInto(&s)
153	return s.AuthorizedToken, err
154}
155
156// AccessToken represents an AccessToken response as a struct.
157type AccessToken struct {
158	ID                string     `json:"id"`
159	ConsumerID        string     `json:"consumer_id"`
160	ProjectID         string     `json:"project_id"`
161	AuthorizingUserID string     `json:"authorizing_user_id"`
162	ExpiresAt         *time.Time `json:"-"`
163}
164
165func (r *AccessToken) UnmarshalJSON(b []byte) error {
166	type tmp AccessToken
167	var s struct {
168		tmp
169		ExpiresAt *gophercloud.JSONRFC3339Milli `json:"expires_at"`
170	}
171	err := json.Unmarshal(b, &s)
172	if err != nil {
173		return err
174	}
175	*r = AccessToken(s.tmp)
176
177	if s.ExpiresAt != nil {
178		t := time.Time(*s.ExpiresAt)
179		r.ExpiresAt = &t
180	}
181
182	return nil
183}
184
185type GetAccessTokenResult struct {
186	gophercloud.Result
187}
188
189// Extract interprets any GetAccessTokenResult result as an AccessToken.
190func (r GetAccessTokenResult) Extract() (*AccessToken, error) {
191	var s struct {
192		AccessToken *AccessToken `json:"access_token"`
193	}
194	err := r.ExtractInto(&s)
195	return s.AccessToken, err
196}
197
198// RevokeAccessTokenResult is the response from a Delete operation. Call its
199// ExtractErr to determine if the request succeeded or failed.
200type RevokeAccessTokenResult struct {
201	gophercloud.ErrResult
202}
203
204// AccessTokensPage is a single page of Access Tokens results.
205type AccessTokensPage struct {
206	pagination.LinkedPageBase
207}
208
209// IsEmpty determines whether or not a an AccessTokensPage contains any results.
210func (r AccessTokensPage) IsEmpty() (bool, error) {
211	accessTokens, err := ExtractAccessTokens(r)
212	return len(accessTokens) == 0, err
213}
214
215// NextPageURL extracts the "next" link from the links section of the result.
216func (r AccessTokensPage) NextPageURL() (string, error) {
217	var s struct {
218		Links struct {
219			Next     string `json:"next"`
220			Previous string `json:"previous"`
221		} `json:"links"`
222	}
223	err := r.ExtractInto(&s)
224	if err != nil {
225		return "", err
226	}
227	return s.Links.Next, err
228}
229
230// ExtractAccessTokens returns a slice of AccessTokens contained in a single
231// page of results.
232func ExtractAccessTokens(r pagination.Page) ([]AccessToken, error) {
233	var s struct {
234		AccessTokens []AccessToken `json:"access_tokens"`
235	}
236	err := (r.(AccessTokensPage)).ExtractInto(&s)
237	return s.AccessTokens, err
238}
239
240// AccessTokenRole represents an Access Token Role struct.
241type AccessTokenRole struct {
242	ID       string `json:"id"`
243	Name     string `json:"name"`
244	DomainID string `json:"domain_id"`
245}
246
247// AccessTokenRolesPage is a single page of Access Token roles results.
248type AccessTokenRolesPage struct {
249	pagination.LinkedPageBase
250}
251
252// IsEmpty determines whether or not a an AccessTokensPage contains any results.
253func (r AccessTokenRolesPage) IsEmpty() (bool, error) {
254	accessTokenRoles, err := ExtractAccessTokenRoles(r)
255	return len(accessTokenRoles) == 0, err
256}
257
258// NextPageURL extracts the "next" link from the links section of the result.
259func (r AccessTokenRolesPage) NextPageURL() (string, error) {
260	var s struct {
261		Links struct {
262			Next     string `json:"next"`
263			Previous string `json:"previous"`
264		} `json:"links"`
265	}
266	err := r.ExtractInto(&s)
267	if err != nil {
268		return "", err
269	}
270	return s.Links.Next, err
271}
272
273// ExtractAccessTokenRoles returns a slice of AccessTokenRole contained in a
274// single page of results.
275func ExtractAccessTokenRoles(r pagination.Page) ([]AccessTokenRole, error) {
276	var s struct {
277		AccessTokenRoles []AccessTokenRole `json:"roles"`
278	}
279	err := (r.(AccessTokenRolesPage)).ExtractInto(&s)
280	return s.AccessTokenRoles, err
281}
282
283type GetAccessTokenRoleResult struct {
284	gophercloud.Result
285}
286
287// Extract interprets any GetAccessTokenRoleResult result as an AccessTokenRole.
288func (r GetAccessTokenRoleResult) Extract() (*AccessTokenRole, error) {
289	var s struct {
290		AccessTokenRole *AccessTokenRole `json:"role"`
291	}
292	err := r.ExtractInto(&s)
293	return s.AccessTokenRole, err
294}
295
296// OAuth1 is an OAuth1 object, returned in OAuth1 token result.
297type OAuth1 struct {
298	AccessTokenID string `json:"access_token_id"`
299	ConsumerID    string `json:"consumer_id"`
300}
301
302// TokenExt represents an extension of the base token result.
303type TokenExt struct {
304	OAuth1 OAuth1 `json:"OS-OAUTH1"`
305}
306