Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-proxy/pkg/proxy/auth_test.go
2500 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package proxy
6
7
import (
8
"fmt"
9
"net/http"
10
"net/http/httptest"
11
"strconv"
12
"strings"
13
"testing"
14
15
"github.com/google/go-cmp/cmp"
16
"github.com/gorilla/mux"
17
"github.com/sirupsen/logrus"
18
19
"github.com/gitpod-io/gitpod/common-go/log"
20
"github.com/gitpod-io/gitpod/ws-manager/api"
21
"github.com/gitpod-io/gitpod/ws-proxy/pkg/common"
22
)
23
24
func TestWorkspaceAuthHandler(t *testing.T) {
25
log.Log.Logger.SetLevel(logrus.PanicLevel)
26
type testResult struct {
27
HandlerCalled bool
28
StatusCode int
29
}
30
31
const (
32
domain = "test-domain.com"
33
workspaceID = "workspac-65f4-43c9-bf46-3541b89dca85"
34
instanceID = "instance-fce1-4ff6-9364-cf6dff0c4ecf"
35
ownerToken = "owner-token"
36
testPort = 8080
37
)
38
var (
39
ownerOnlyInfos = []common.WorkspaceInfo{
40
{
41
WorkspaceID: workspaceID,
42
InstanceID: instanceID,
43
Auth: &api.WorkspaceAuthentication{
44
Admission: api.AdmissionLevel_ADMIT_OWNER_ONLY,
45
OwnerToken: ownerToken,
46
},
47
Ports: []*api.PortSpec{{Port: testPort, Visibility: api.PortVisibility_PORT_VISIBILITY_PRIVATE}},
48
},
49
}
50
publicPortInfos = []common.WorkspaceInfo{
51
{
52
WorkspaceID: workspaceID,
53
InstanceID: instanceID,
54
Auth: &api.WorkspaceAuthentication{
55
Admission: api.AdmissionLevel_ADMIT_OWNER_ONLY,
56
OwnerToken: ownerToken,
57
},
58
Ports: []*api.PortSpec{{Port: testPort, Visibility: api.PortVisibility_PORT_VISIBILITY_PUBLIC}},
59
},
60
}
61
admitEveryoneInfos = []common.WorkspaceInfo{
62
{
63
WorkspaceID: workspaceID,
64
InstanceID: instanceID,
65
Auth: &api.WorkspaceAuthentication{Admission: api.AdmissionLevel_ADMIT_EVERYONE},
66
},
67
}
68
)
69
tests := []struct {
70
Name string
71
Infos []common.WorkspaceInfo
72
OwnerCookie string
73
WorkspaceID string
74
Port string
75
Expected testResult
76
}{
77
{
78
Name: "workspace not found",
79
WorkspaceID: workspaceID,
80
Expected: testResult{
81
HandlerCalled: false,
82
StatusCode: http.StatusNotFound,
83
},
84
},
85
{
86
Name: "no workspace",
87
Expected: testResult{
88
HandlerCalled: false,
89
StatusCode: http.StatusForbidden,
90
},
91
},
92
{
93
Name: "no credentials",
94
Infos: ownerOnlyInfos,
95
WorkspaceID: workspaceID,
96
Expected: testResult{
97
HandlerCalled: false,
98
StatusCode: http.StatusUnauthorized,
99
},
100
},
101
{
102
Name: "wrong credentials",
103
Infos: ownerOnlyInfos,
104
WorkspaceID: workspaceID,
105
OwnerCookie: "this is the wrong value",
106
Expected: testResult{
107
HandlerCalled: false,
108
StatusCode: http.StatusForbidden,
109
},
110
},
111
{
112
Name: "broken credentials",
113
Infos: ownerOnlyInfos,
114
WorkspaceID: workspaceID,
115
OwnerCookie: "%^? is invalid encoding ",
116
Expected: testResult{
117
HandlerCalled: false,
118
StatusCode: http.StatusBadRequest,
119
},
120
},
121
{
122
Name: "correct credentials",
123
Infos: ownerOnlyInfos,
124
WorkspaceID: workspaceID,
125
OwnerCookie: ownerToken,
126
Expected: testResult{
127
HandlerCalled: true,
128
StatusCode: http.StatusOK,
129
},
130
},
131
{
132
Name: "admit everyone without cookie",
133
Infos: admitEveryoneInfos,
134
WorkspaceID: workspaceID,
135
Expected: testResult{
136
HandlerCalled: true,
137
StatusCode: http.StatusOK,
138
},
139
},
140
{
141
Name: "admit everyone with cookie",
142
Infos: admitEveryoneInfos,
143
WorkspaceID: workspaceID,
144
OwnerCookie: ownerToken,
145
Expected: testResult{
146
HandlerCalled: true,
147
StatusCode: http.StatusOK,
148
},
149
},
150
{
151
Name: "admit everyone with wrong cookie",
152
Infos: admitEveryoneInfos,
153
WorkspaceID: workspaceID,
154
OwnerCookie: ownerToken + "-this-is-wrong",
155
Expected: testResult{
156
HandlerCalled: true,
157
StatusCode: http.StatusOK,
158
},
159
},
160
{
161
Name: "private port",
162
Infos: ownerOnlyInfos,
163
WorkspaceID: workspaceID,
164
OwnerCookie: ownerToken,
165
Port: strconv.Itoa(testPort),
166
Expected: testResult{
167
HandlerCalled: true,
168
StatusCode: http.StatusOK,
169
},
170
},
171
{
172
Name: "private port without cookie",
173
Infos: ownerOnlyInfos,
174
WorkspaceID: workspaceID,
175
Port: strconv.Itoa(testPort),
176
Expected: testResult{
177
HandlerCalled: false,
178
StatusCode: http.StatusUnauthorized,
179
},
180
},
181
{
182
Name: "private port with wrong cookie",
183
Infos: ownerOnlyInfos,
184
WorkspaceID: workspaceID,
185
OwnerCookie: ownerToken + "-this-is-wrong",
186
Port: strconv.Itoa(testPort),
187
Expected: testResult{
188
HandlerCalled: false,
189
StatusCode: http.StatusForbidden,
190
},
191
},
192
{
193
Name: "public port",
194
Infos: publicPortInfos,
195
WorkspaceID: workspaceID,
196
Port: strconv.Itoa(testPort),
197
Expected: testResult{
198
HandlerCalled: true,
199
StatusCode: http.StatusOK,
200
},
201
},
202
{
203
Name: "broken port",
204
Infos: publicPortInfos,
205
WorkspaceID: workspaceID,
206
Port: "not a valid number",
207
OwnerCookie: ownerToken,
208
Expected: testResult{
209
HandlerCalled: true,
210
StatusCode: http.StatusOK,
211
},
212
},
213
{
214
Name: "broken port without cookie",
215
Infos: publicPortInfos,
216
WorkspaceID: workspaceID,
217
Port: "not a valid number",
218
Expected: testResult{
219
HandlerCalled: false,
220
StatusCode: http.StatusUnauthorized,
221
},
222
},
223
}
224
225
for _, test := range tests {
226
t.Run(test.Name, func(t *testing.T) {
227
var res testResult
228
handler := WorkspaceAuthHandler(domain, &fakeWsInfoProvider{infos: test.Infos})(http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
229
res.HandlerCalled = true
230
resp.WriteHeader(http.StatusOK)
231
}))
232
233
rr := httptest.NewRecorder()
234
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://%s/", domain), nil)
235
if test.OwnerCookie != "" {
236
setOwnerTokenCookie(req, domain, instanceID, test.OwnerCookie)
237
}
238
vars := map[string]string{
239
common.WorkspaceIDIdentifier: test.WorkspaceID,
240
}
241
if test.Port != "" {
242
vars[common.WorkspacePortIdentifier] = test.Port
243
}
244
req = mux.SetURLVars(req, vars)
245
246
handler.ServeHTTP(rr, req)
247
res.StatusCode = rr.Code
248
249
if diff := cmp.Diff(test.Expected, res); diff != "" {
250
t.Errorf("unexpected response (-want +got):\n%s", diff)
251
}
252
})
253
}
254
}
255
256
func setOwnerTokenCookie(r *http.Request, domain, instanceID, token string) {
257
c := ownerTokenCookie(domain, instanceID, token)
258
r.AddCookie(c)
259
}
260
261
func ownerTokenCookie(domain, instanceID, token string) *http.Cookie {
262
domainPart := strings.ReplaceAll(domain, ".", "_")
263
domainPart = strings.ReplaceAll(domainPart, "-", "_")
264
return &http.Cookie{Name: "_" + domainPart + "_ws_" + instanceID + "_owner_", Value: token}
265
}
266
267