1package cf
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7	"net/http"
8	"net/http/httptest"
9	"reflect"
10	"strings"
11	"testing"
12
13	"github.com/concourse/dex/connector"
14	"github.com/sirupsen/logrus"
15)
16
17func TestOpen(t *testing.T) {
18	testServer := testSetup()
19	defer testServer.Close()
20
21	conn := newConnector(t, testServer.URL)
22
23	expectEqual(t, conn.clientID, "test-client")
24	expectEqual(t, conn.clientSecret, "secret")
25	expectEqual(t, conn.redirectURI, testServer.URL+"/callback")
26}
27
28func TestHandleCallback(t *testing.T) {
29
30	testServer := testSetup()
31	defer testServer.Close()
32
33	cfConn := &cfConnector{
34		tokenURL:         fmt.Sprintf("%s/token", testServer.URL),
35		authorizationURL: fmt.Sprintf("%s/authorize", testServer.URL),
36		userInfoURL:      fmt.Sprintf("%s/userinfo", testServer.URL),
37		apiURL:           testServer.URL,
38		clientSecret:     "secret",
39		clientID:         "test-client",
40		redirectURI:      "localhost:8080/sky/dex/callback",
41		httpClient:       http.DefaultClient,
42	}
43
44	req, err := http.NewRequest("GET", testServer.URL, nil)
45	expectEqual(t, err, nil)
46
47	t.Run("CallbackWithGroupsScope", func(t *testing.T) {
48		identity, err := cfConn.HandleCallback(connector.Scopes{Groups: true}, req)
49		expectEqual(t, err, nil)
50
51		expectEqual(t, len(identity.Groups), 24)
52		expectEqual(t, identity.Groups[0], "some-org-guid-1")
53		expectEqual(t, identity.Groups[1], "some-org-guid-2")
54		expectEqual(t, identity.Groups[2], "some-org-guid-3")
55		expectEqual(t, identity.Groups[3], "some-org-guid-4")
56		expectEqual(t, identity.Groups[4], "some-org-name-1")
57		expectEqual(t, identity.Groups[5], "some-org-name-1:some-space-name-1")
58		expectEqual(t, identity.Groups[6], "some-org-name-1:some-space-name-1:auditor")
59		expectEqual(t, identity.Groups[7], "some-org-name-1:some-space-name-1:developer")
60		expectEqual(t, identity.Groups[8], "some-org-name-1:some-space-name-1:manager")
61		expectEqual(t, identity.Groups[9], "some-org-name-2")
62		expectEqual(t, identity.Groups[10], "some-org-name-2:some-space-name-2")
63		expectEqual(t, identity.Groups[11], "some-org-name-2:some-space-name-2:auditor")
64		expectEqual(t, identity.Groups[12], "some-org-name-2:some-space-name-2:developer")
65		expectEqual(t, identity.Groups[13], "some-org-name-2:some-space-name-2:manager")
66		expectEqual(t, identity.Groups[14], "some-org-name-3")
67		expectEqual(t, identity.Groups[15], "some-org-name-4")
68		expectEqual(t, identity.Groups[16], "some-space-guid-1")
69		expectEqual(t, identity.Groups[17], "some-space-guid-1:auditor")
70		expectEqual(t, identity.Groups[18], "some-space-guid-1:developer")
71		expectEqual(t, identity.Groups[19], "some-space-guid-1:manager")
72		expectEqual(t, identity.Groups[20], "some-space-guid-2")
73		expectEqual(t, identity.Groups[21], "some-space-guid-2:auditor")
74		expectEqual(t, identity.Groups[22], "some-space-guid-2:developer")
75		expectEqual(t, identity.Groups[23], "some-space-guid-2:manager")
76	})
77
78	t.Run("CallbackWithoutGroupsScope", func(t *testing.T) {
79		identity, err := cfConn.HandleCallback(connector.Scopes{}, req)
80
81		expectEqual(t, err, nil)
82		expectEqual(t, identity.UserID, "12345")
83		expectEqual(t, identity.Username, "test-user")
84	})
85
86	t.Run("CallbackWithOfflineAccessScope", func(t *testing.T) {
87		identity, err := cfConn.HandleCallback(connector.Scopes{OfflineAccess: true}, req)
88
89		expectEqual(t, err, nil)
90		expectNotEqual(t, len(identity.ConnectorData), 0)
91
92		cData := connectorData{}
93		err = json.Unmarshal(identity.ConnectorData, &cData)
94
95		expectEqual(t, err, nil)
96		expectNotEqual(t, cData.AccessToken, "")
97	})
98}
99
100func testSpaceHandler(reqUrl, spaceApiEndpoint string) (result map[string]interface{}) {
101	fullUrl := fmt.Sprintf("%s?order-direction=asc&page=2&results-per-page=50", spaceApiEndpoint)
102	if strings.Contains(reqUrl, fullUrl) {
103		result = map[string]interface{}{
104			"resources": []map[string]interface{}{
105				{
106					"metadata": map[string]string{"guid": "some-space-guid-2"},
107					"entity":   map[string]string{"name": "some-space-name-2", "organization_guid": "some-org-guid-2"},
108				},
109			},
110		}
111	} else {
112		nextUrl := fmt.Sprintf("/v2/users/12345/%s?order-direction=asc&page=2&results-per-page=50", spaceApiEndpoint)
113		result = map[string]interface{}{
114			"next_url": nextUrl,
115			"resources": []map[string]interface{}{
116				{
117					"metadata": map[string]string{"guid": "some-space-guid-1"},
118					"entity":   map[string]string{"name": "some-space-name-1", "organization_guid": "some-org-guid-1"},
119				},
120			},
121		}
122	}
123	return result
124}
125
126func testOrgHandler(reqUrl string) (result map[string]interface{}) {
127	if strings.Contains(reqUrl, "organizations?order-direction=asc&page=2&results-per-page=50") {
128		result = map[string]interface{}{
129			"resources": []map[string]interface{}{
130				{
131					"metadata": map[string]string{"guid": "some-org-guid-3"},
132					"entity":   map[string]string{"name": "some-org-name-3"},
133				},
134				{
135					"metadata": map[string]string{"guid": "some-org-guid-4"},
136					"entity":   map[string]string{"name": "some-org-name-4"},
137				},
138			},
139		}
140	} else {
141		result = map[string]interface{}{
142			"next_url": "/v2/users/12345/organizations?order-direction=asc&page=2&results-per-page=50",
143			"resources": []map[string]interface{}{
144				{
145					"metadata": map[string]string{"guid": "some-org-guid-1"},
146					"entity":   map[string]string{"name": "some-org-name-1"},
147				},
148				{
149					"metadata": map[string]string{"guid": "some-org-guid-2"},
150					"entity":   map[string]string{"name": "some-org-name-2"},
151				},
152			},
153		}
154	}
155	return result
156}
157
158func testSetup() *httptest.Server {
159	mux := http.NewServeMux()
160	mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
161		token := "eyJhbGciOiJSUzI1NiIsImtpZCI6ImtleS0xIiwidHlwIjoiSldUIn0.eyJqdGkiOiIxMjk4MTNhZjJiNGM0ZDNhYmYyNjljMzM4OTFkZjNiZCIsInN1YiI6ImNmMWFlODk4LWQ1ODctNDBhYS1hNWRiLTE5ZTY3MjI0N2I1NyIsInNjb3BlIjpbImNsb3VkX2NvbnRyb2xsZXIucmVhZCIsIm9wZW5pZCJdLCJjbGllbnRfaWQiOiJjb25jb3Vyc2UiLCJjaWQiOiJjb25jb3Vyc2UiLCJhenAiOiJjb25jb3Vyc2UiLCJncmFudF90eXBlIjoiYXV0aG9yaXphdGlvbl9jb2RlIiwidXNlcl9pZCI6ImNmMWFlODk4LWQ1ODctNDBhYS1hNWRiLTE5ZTY3MjI0N2I1NyIsIm9yaWdpbiI6InVhYSIsInVzZXJfbmFtZSI6ImFkbWluIiwiZW1haWwiOiJhZG1pbiIsImF1dGhfdGltZSI6MTUyMzM3NDIwNCwicmV2X3NpZyI6IjYxNWJjMTk0IiwiaWF0IjoxNTIzMzc3MTUyLCJleHAiOjE1MjM0MjAzNTIsImlzcyI6Imh0dHBzOi8vdWFhLnN0eXgucHVzaC5nY3AuY2YtYXBwLmNvbS9vYXV0aC90b2tlbiIsInppZCI6InVhYSIsImF1ZCI6WyJjbG91ZF9jb250cm9sbGVyIiwiY29uY291cnNlIiwib3BlbmlkIl19.FslbnwvW0WScVRNK8IWghRX0buXfl6qaI1K7z_dzjPUVrdEyMtaYa3kJI8srA-2G1PjSSEWa_3Vzs_BEnTc3iG0JQWU0XlcjdCdAFTvnmKiHSzffy1O_oGYyH47KXtnZOxHf3rdV_Xgw4XTqPrfKXQxnPemUAJyKf2tjgs3XToGaqqBw-D_2BQVY79kF0_GgksQsViqq1GW0Dur6m2CgBhtc2h1AQGO16izXl3uNbpW6ClhaW43NQXlE4wqtr7kfmxyOigHJb2MSQ3wwPc6pqYdUT6ka_TMqavqbxEJ4QcS6SoEcVsDTmEQ4c8dmWUgXM0AZjd0CaEGTB6FDHxH5sw"
162		w.Header().Add("Content-Type", "application/json")
163		json.NewEncoder(w).Encode(map[string]string{
164			"access_token": token,
165		})
166	})
167
168	mux.HandleFunc("/v2/info", func(w http.ResponseWriter, r *http.Request) {
169		url := fmt.Sprintf("http://%s", r.Host)
170
171		json.NewEncoder(w).Encode(map[string]string{
172			"authorization_endpoint": url,
173		})
174	})
175
176	mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
177		url := fmt.Sprintf("http://%s", r.Host)
178
179		json.NewEncoder(w).Encode(map[string]string{
180			"token_endpoint":         url,
181			"authorization_endpoint": url,
182			"userinfo_endpoint":      url,
183		})
184	})
185
186	mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
187	})
188
189	mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) {
190		json.NewEncoder(w).Encode(map[string]string{
191			"user_id":   "12345",
192			"user_name": "test-user",
193			"email":     "blah-email",
194		})
195	})
196
197	mux.HandleFunc("/v2/users/", func(w http.ResponseWriter, r *http.Request) {
198		var result map[string]interface{}
199
200		reqUrl := r.URL.String()
201		if strings.Contains(reqUrl, "/spaces") {
202			result = testSpaceHandler(reqUrl, "spaces")
203		}
204
205		if strings.Contains(reqUrl, "/audited_spaces") {
206			result = testSpaceHandler(reqUrl, "audited_spaces")
207		}
208
209		if strings.Contains(reqUrl, "/managed_spaces") {
210			result = testSpaceHandler(reqUrl, "managed_spaces")
211		}
212
213		if strings.Contains(reqUrl, "organizations") {
214			result = testOrgHandler(reqUrl)
215		}
216
217		json.NewEncoder(w).Encode(result)
218	})
219
220	return httptest.NewServer(mux)
221}
222
223func newConnector(t *testing.T, serverURL string) *cfConnector {
224
225	callBackURL := fmt.Sprintf("%s/callback", serverURL)
226
227	testConfig := Config{
228		APIURL:             serverURL,
229		ClientID:           "test-client",
230		ClientSecret:       "secret",
231		RedirectURI:        callBackURL,
232		InsecureSkipVerify: true,
233	}
234
235	log := logrus.New()
236
237	conn, err := testConfig.Open("id", log)
238	if err != nil {
239		t.Fatal(err)
240	}
241
242	cfConn, ok := conn.(*cfConnector)
243	if !ok {
244		t.Fatal(errors.New("it is not a cf conn"))
245	}
246
247	return cfConn
248}
249
250func expectEqual(t *testing.T, a interface{}, b interface{}) {
251	if !reflect.DeepEqual(a, b) {
252		t.Fatalf("Expected %+v to equal %+v", a, b)
253	}
254}
255
256func expectNotEqual(t *testing.T, a interface{}, b interface{}) {
257	if reflect.DeepEqual(a, b) {
258		t.Fatalf("Expected %+v to NOT equal %+v", a, b)
259	}
260}
261