1//
2// Copyright (c) 2018, Joyent, Inc. All rights reserved.
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at http://mozilla.org/MPL/2.0/.
7//
8
9package identity_test
10
11import (
12	"context"
13	"errors"
14	"fmt"
15	"io/ioutil"
16	"net/http"
17	"strings"
18	"testing"
19
20	"github.com/joyent/triton-go/identity"
21	"github.com/joyent/triton-go/testutils"
22)
23
24var (
25	fakePolicyId           = "95ca7b25-5c8f-4c1b-92da-4276f23805ds"
26	aDifferentFakePolicyId = "95ca7b25-5c8f-4c1b-92da-4276f23807f3"
27	listPoliciesErrorType  = errors.New("unable to list policies")
28	getPolicyErrorType     = errors.New("unable to get policy")
29	deletePolicyErrorType  = errors.New("unable to delete policy")
30	updatePolicyErrorType  = errors.New("unable to update policy")
31	createPolicyErrorType  = errors.New("unable to create policy")
32)
33
34func TestListPolicies(t *testing.T) {
35	identityClient := MockIdentityClient()
36
37	do := func(ctx context.Context, ic *identity.IdentityClient) ([]*identity.Policy, error) {
38		defer testutils.DeactivateClient()
39
40		policies, err := ic.Policies().List(ctx, &identity.ListPoliciesInput{})
41		if err != nil {
42			return nil, err
43		}
44
45		return policies, nil
46	}
47
48	t.Run("successful", func(t *testing.T) {
49		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies", accountUrl), listPoliciesSuccess)
50
51		resp, err := do(context.Background(), identityClient)
52		if err != nil {
53			t.Fatal(err)
54		}
55
56		if resp == nil {
57			t.Fatalf("Expected an output but got nil")
58		}
59	})
60
61	t.Run("eof", func(t *testing.T) {
62		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies", accountUrl), listPoliciesEmpty)
63
64		_, err := do(context.Background(), identityClient)
65		if err == nil {
66			t.Fatal(err)
67		}
68
69		if !strings.Contains(err.Error(), "EOF") {
70			t.Errorf("expected error to contain EOF: found %s", err)
71		}
72	})
73
74	t.Run("bad_decode", func(t *testing.T) {
75		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies", accountUrl), listPoliciesBadeDecode)
76
77		_, err := do(context.Background(), identityClient)
78		if err == nil {
79			t.Fatal(err)
80		}
81
82		if !strings.Contains(err.Error(), "invalid character") {
83			t.Errorf("expected decode to fail: found %s", err)
84		}
85	})
86
87	t.Run("error", func(t *testing.T) {
88		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies", accountUrl), listPoliciesError)
89
90		resp, err := do(context.Background(), identityClient)
91		if err == nil {
92			t.Fatal(err)
93		}
94		if resp != nil {
95			t.Error("expected resp to be nil")
96		}
97
98		if !strings.Contains(err.Error(), "unable to list policies") {
99			t.Errorf("expected error to equal testError: found %s", err)
100		}
101	})
102}
103
104func TestGetPolicy(t *testing.T) {
105	identityClient := MockIdentityClient()
106
107	do := func(ctx context.Context, ic *identity.IdentityClient) (*identity.Policy, error) {
108		defer testutils.DeactivateClient()
109
110		user, err := ic.Policies().Get(ctx, &identity.GetPolicyInput{
111			PolicyID: fakePolicyId,
112		})
113		if err != nil {
114			return nil, err
115		}
116
117		return user, nil
118	}
119
120	t.Run("successful", func(t *testing.T) {
121		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies/%s", accountUrl, fakePolicyId), getPolicySuccess)
122
123		resp, err := do(context.Background(), identityClient)
124		if err != nil {
125			t.Fatal(err)
126		}
127
128		if resp == nil {
129			t.Fatalf("Expected an output but got nil")
130		}
131	})
132
133	t.Run("eof", func(t *testing.T) {
134		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies/%s", accountUrl, fakePolicyId), getPolicyEmpty)
135
136		_, err := do(context.Background(), identityClient)
137		if err == nil {
138			t.Fatal(err)
139		}
140
141		if !strings.Contains(err.Error(), "EOF") {
142			t.Errorf("expected error to contain EOF: found %s", err)
143		}
144	})
145
146	t.Run("bad_decode", func(t *testing.T) {
147		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies/%s", accountUrl, fakePolicyId), getPolicyBadeDecode)
148
149		_, err := do(context.Background(), identityClient)
150		if err == nil {
151			t.Fatal(err)
152		}
153
154		if !strings.Contains(err.Error(), "invalid character") {
155			t.Errorf("expected decode to fail: found %s", err)
156		}
157	})
158
159	t.Run("error", func(t *testing.T) {
160		testutils.RegisterResponder("GET", fmt.Sprintf("/%s/policies", accountUrl), getPolicyError)
161
162		resp, err := do(context.Background(), identityClient)
163		if err == nil {
164			t.Fatal(err)
165		}
166		if resp != nil {
167			t.Error("expected resp to be nil")
168		}
169
170		if !strings.Contains(err.Error(), "unable to get policy") {
171			t.Errorf("expected error to equal testError: found %s", err)
172		}
173	})
174}
175
176func TestDeletePolicy(t *testing.T) {
177	identityClient := MockIdentityClient()
178
179	do := func(ctx context.Context, ic *identity.IdentityClient) error {
180		defer testutils.DeactivateClient()
181
182		return ic.Policies().Delete(ctx, &identity.DeletePolicyInput{
183			PolicyID: fakePolicyId,
184		})
185	}
186
187	t.Run("successful", func(t *testing.T) {
188		testutils.RegisterResponder("DELETE", fmt.Sprintf("/%s/policies/%s", accountUrl, fakePolicyId), deletePolicySuccess)
189
190		err := do(context.Background(), identityClient)
191		if err != nil {
192			t.Fatal(err)
193		}
194	})
195
196	t.Run("error", func(t *testing.T) {
197		testutils.RegisterResponder("DELETE", fmt.Sprintf("/%s/policies", accountUrl), deletePolicyError)
198
199		err := do(context.Background(), identityClient)
200		if err == nil {
201			t.Fatal(err)
202		}
203
204		if !strings.Contains(err.Error(), "unable to delete policy") {
205			t.Errorf("expected error to equal testError: found %s", err)
206		}
207	})
208}
209
210func TestUpdatePolicy(t *testing.T) {
211	identityClient := MockIdentityClient()
212
213	do := func(ctx context.Context, ic *identity.IdentityClient) (*identity.Policy, error) {
214		defer testutils.DeactivateClient()
215
216		policy, err := ic.Policies().Update(ctx, &identity.UpdatePolicyInput{
217			PolicyID:    fakePolicyId,
218			Description: "Updated Description",
219			Name:        "Updated Name",
220		})
221		if err != nil {
222			return nil, err
223		}
224
225		return policy, nil
226	}
227
228	t.Run("successful", func(t *testing.T) {
229		testutils.RegisterResponder("POST", fmt.Sprintf("/%s/policies/%s", accountUrl, fakePolicyId), updatePolicySuccess)
230
231		_, err := do(context.Background(), identityClient)
232		if err != nil {
233			t.Fatal(err)
234		}
235	})
236
237	t.Run("error", func(t *testing.T) {
238		testutils.RegisterResponder("POST", fmt.Sprintf("/%s/policies/%s", accountUrl, aDifferentFakePolicyId), updatePolicyError)
239
240		_, err := do(context.Background(), identityClient)
241		if err == nil {
242			t.Fatal(err)
243		}
244
245		if !strings.Contains(err.Error(), "unable to update policy") {
246			t.Errorf("expected error to equal testError: found %s", err)
247		}
248	})
249}
250
251func TestCreatePolicy(t *testing.T) {
252	identityClient := MockIdentityClient()
253
254	do := func(ctx context.Context, ic *identity.IdentityClient) (*identity.Policy, error) {
255		defer testutils.DeactivateClient()
256
257		policy, err := ic.Policies().Create(ctx, &identity.CreatePolicyInput{
258			Name:        "Test Policy",
259			Description: "Test Description",
260			Rules:       []string{"CAN rebootmachine"},
261		})
262
263		if err != nil {
264			return nil, err
265		}
266
267		return policy, nil
268	}
269
270	t.Run("successful", func(t *testing.T) {
271		testutils.RegisterResponder("POST", fmt.Sprintf("/%s/policies", accountUrl), createPolicySuccess)
272
273		_, err := do(context.Background(), identityClient)
274		if err != nil {
275			t.Fatal(err)
276		}
277	})
278
279	t.Run("error", func(t *testing.T) {
280		testutils.RegisterResponder("POST", fmt.Sprintf("/%s/policies", accountUrl), createPolicyError)
281
282		_, err := do(context.Background(), identityClient)
283		if err == nil {
284			t.Fatal(err)
285		}
286
287		if !strings.Contains(err.Error(), "unable to create policy") {
288			t.Errorf("expected error to equal testError: found %s", err)
289		}
290	})
291}
292
293func listPoliciesEmpty(req *http.Request) (*http.Response, error) {
294	header := http.Header{}
295	header.Add("Content-Type", "application/json")
296
297	return &http.Response{
298		StatusCode: 200,
299		Header:     header,
300		Body:       ioutil.NopCloser(strings.NewReader("")),
301	}, nil
302}
303
304func listPoliciesSuccess(req *http.Request) (*http.Response, error) {
305	header := http.Header{}
306	header.Add("Content-Type", "application/json")
307
308	body := strings.NewReader(`[
309	{
310    "name": "readinstance",
311    "id": "95ca7b25-5c8f-4c1b-92da-4276f23807f3",
312    "rules": [
313      "can listmachine and getmachine"
314    ]
315  },
316  {
317    "name": "createinstance",
318    "id": "95ca7b25-5c8f-4c1b-92da-4276f23805ds",
319    "rules": [
320      "can createinstance"
321    ]
322  }
323]`)
324
325	return &http.Response{
326		StatusCode: 200,
327		Header:     header,
328		Body:       ioutil.NopCloser(body),
329	}, nil
330}
331
332func listPoliciesBadeDecode(req *http.Request) (*http.Response, error) {
333	header := http.Header{}
334	header.Add("Content-Type", "application/json")
335
336	body := strings.NewReader(`{[
337	{
338    "name": "createinstance",
339    "id": "95ca7b25-5c8f-4c1b-92da-4276f23805ds",
340    "rules": [
341      "can createinstance"
342    ]
343  }
344]}`)
345
346	return &http.Response{
347		StatusCode: 200,
348		Header:     header,
349		Body:       ioutil.NopCloser(body),
350	}, nil
351}
352
353func listPoliciesError(req *http.Request) (*http.Response, error) {
354	return nil, listPoliciesErrorType
355}
356
357func getPolicySuccess(req *http.Request) (*http.Response, error) {
358	header := http.Header{}
359	header.Add("Content-Type", "application/json")
360
361	body := strings.NewReader(`{
362    "name": "readinstance",
363    "id": "95ca7b25-5c8f-4c1b-92da-4276f23807f3",
364    "rules": [
365      "can listmachine and getmachine"
366    ]
367  }
368`)
369
370	return &http.Response{
371		StatusCode: 200,
372		Header:     header,
373		Body:       ioutil.NopCloser(body),
374	}, nil
375}
376
377func getPolicyError(req *http.Request) (*http.Response, error) {
378	return nil, getPolicyErrorType
379}
380
381func getPolicyBadeDecode(req *http.Request) (*http.Response, error) {
382	header := http.Header{}
383	header.Add("Content-Type", "application/json")
384
385	body := strings.NewReader(`{
386    "name": "readinstance",
387    "id": "95ca7b25-5c8f-4c1b-92da-4276f23807f3",
388    "rules": [
389      "can listmachine and getmachine"
390    ],
391  }`)
392
393	return &http.Response{
394		StatusCode: 200,
395		Header:     header,
396		Body:       ioutil.NopCloser(body),
397	}, nil
398}
399
400func getPolicyEmpty(req *http.Request) (*http.Response, error) {
401	header := http.Header{}
402	header.Add("Content-Type", "application/json")
403
404	return &http.Response{
405		StatusCode: 200,
406		Header:     header,
407		Body:       ioutil.NopCloser(strings.NewReader("")),
408	}, nil
409}
410
411func deletePolicySuccess(req *http.Request) (*http.Response, error) {
412	header := http.Header{}
413	header.Add("Content-Type", "application/json")
414
415	return &http.Response{
416		StatusCode: 204,
417		Header:     header,
418	}, nil
419}
420
421func deletePolicyError(req *http.Request) (*http.Response, error) {
422	return nil, deletePolicyErrorType
423}
424
425func updatePolicySuccess(req *http.Request) (*http.Response, error) {
426	header := http.Header{}
427	header.Add("Content-Type", "application/json")
428
429	body := strings.NewReader(`{
430  "name": "Updated Name",
431  "id": "95ca7b25-5c8f-4c1b-92da-4276f23807f3",
432  "rules": [
433    "can rebootMachine"
434  ],
435  "description": "Updated Description"
436}
437`)
438
439	return &http.Response{
440		StatusCode: 200,
441		Header:     header,
442		Body:       ioutil.NopCloser(body),
443	}, nil
444}
445
446func updatePolicyError(req *http.Request) (*http.Response, error) {
447	return nil, updatePolicyErrorType
448}
449
450func createPolicySuccess(req *http.Request) (*http.Response, error) {
451	header := http.Header{}
452	header.Add("Content-Type", "application/json")
453
454	body := strings.NewReader(`{
455  "name": "Test Policy",
456  "id": "95ca7b25-5c8f-4c1b-92da-4276f23807f3",
457  "rules": [
458    "CAN rebootmachine"
459  ],
460  "description": "Test Description"
461}
462`)
463
464	return &http.Response{
465		StatusCode: 201,
466		Header:     header,
467		Body:       ioutil.NopCloser(body),
468	}, nil
469}
470
471func createPolicyError(req *http.Request) (*http.Response, error) {
472	return nil, createPolicyErrorType
473}
474