Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/test/tests/smoke-test/collaborator_test.go
2496 views
1
// Copyright (c) 2024 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 smoketest
6
7
import (
8
"context"
9
"fmt"
10
"net/http"
11
"os"
12
"strings"
13
"testing"
14
"time"
15
16
connect "github.com/bufbuild/connect-go"
17
"github.com/gitpod-io/gitpod/common-go/util"
18
experimentalv1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
19
experimentalv1connect "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
20
v1 "github.com/gitpod-io/gitpod/components/public-api/go/v1"
21
v1connect "github.com/gitpod-io/gitpod/components/public-api/go/v1/v1connect"
22
serverapi "github.com/gitpod-io/gitpod/gitpod-protocol"
23
log "github.com/sirupsen/logrus"
24
"golang.org/x/xerrors"
25
"google.golang.org/grpc"
26
"google.golang.org/grpc/credentials/insecure"
27
)
28
29
/**
30
*
31
# 1. Create an org
32
# 2. Invite your another account to be a collaborator of this org
33
# 3. Create a project in this org
34
35
cd test
36
export TEST_COLLABORATOR=true
37
export GITPOD_HOST=hw-collaborator.preview.gitpod-dev.com
38
export ORG_ID=130d67cf-8b11-45e9-b8d2-33bf34ca4a4c
39
export PROJECT_ID=699c6566-1049-469e-9e61-c8fe0db9d396
40
41
# test with cookie, cookie should format like `_hw_collaborator_preview_gitpod_dev_com_jwt2_=xxx“
42
export USE_COOKIE=true
43
export USER_TOKEN="<your_cookie>"
44
go test -run "^TestMembers|TestProjects|TestGetProject$" github.com/gitpod-io/gitpod/test/tests/smoke-test -v -count=1
45
46
# test with PAT
47
export USE_COOKIE=false
48
export USER_TOKEN=<your_pat>
49
go test -run "^TestMembers|TestProjects|TestGetProject$" github.com/gitpod-io/gitpod/test/tests/smoke-test -v -count=1
50
51
# debug: go test -run "^TestProjects$" github.com/gitpod-io/gitpod/test/tests/smoke-test -v -count=1
52
*/
53
54
// TestMembers tests collaborator can't access members with:
55
// 1. experimental.v1 + COOKIE -> permission_denied
56
// 2. experimental.v1 + PAT -> permission_denied
57
// 3. v1 + COOKIE -> permission_denied
58
// 4. v1 + PAT -> permission_denied
59
// 5. server + COOKIE -> code 403
60
func TestMembers(t *testing.T) {
61
if !shouldTestCollaborator() {
62
t.Skip("skipping collaborator test")
63
return
64
}
65
userToken, _ := os.LookupEnv("USER_TOKEN")
66
gitpodHost, _ := os.LookupEnv("GITPOD_HOST")
67
orgID, _ := os.LookupEnv("ORG_ID")
68
69
serverConn, err0 := connectToServer(gitpodHost, userToken)
70
if err0 != nil {
71
t.Errorf("failed getting server conn: %v", err0)
72
}
73
v1Http, v1Opts, v1Host := getPAPIConnSettings(gitpodHost, userToken, getUseCookie(), false)
74
ev1Http, ev1Opts, ev1Host := getPAPIConnSettings(gitpodHost, userToken, getUseCookie(), true)
75
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
76
defer cancel()
77
client := experimentalv1connect.NewTeamsServiceClient(ev1Http, ev1Host, ev1Opts...)
78
v1Client := v1connect.NewOrganizationServiceClient(v1Http, v1Host, v1Opts...)
79
_, err := client.ListTeamMembers(ctx, connect.NewRequest(&experimentalv1.ListTeamMembersRequest{
80
TeamId: orgID,
81
}))
82
_, err2 := v1Client.ListOrganizationMembers(ctx, connect.NewRequest(&v1.ListOrganizationMembersRequest{
83
OrganizationId: orgID,
84
Pagination: &v1.PaginationRequest{},
85
}))
86
_, err3 := serverConn.GetTeamMembers(ctx, orgID)
87
88
if !strings.Contains(err.Error(), "permission_denied:") {
89
t.Errorf("experimental should respond permission_denied, got: %s", err)
90
}
91
if !strings.Contains(err2.Error(), "permission_denied:") {
92
t.Errorf("v1 should respond permission_denied, got: %s", err2)
93
}
94
if getUseCookie() {
95
if !strings.Contains(err3.Error(), "code 403 ") {
96
t.Errorf("server should respond permission_denied, got: %s", err3)
97
}
98
}
99
}
100
101
// TestProjects tests collaborator can't access projects with:
102
// 1. experimental.v1 + COOKIE -> empty array
103
// 2. experimental.v1 + PAT -> empty array
104
// 3. v1 + COOKIE -> empty array
105
// 4. v1 + PAT -> empty array
106
// 5. server + COOKIE -> empty array
107
func TestProjects(t *testing.T) {
108
if !shouldTestCollaborator() {
109
t.Skip("skipping collaborator test")
110
return
111
}
112
userToken, _ := os.LookupEnv("USER_TOKEN")
113
gitpodHost, _ := os.LookupEnv("GITPOD_HOST")
114
orgID, _ := os.LookupEnv("ORG_ID")
115
116
serverConn, err0 := connectToServer(gitpodHost, userToken)
117
if err0 != nil {
118
t.Errorf("failed getting server conn: %v", err0)
119
}
120
v1Http, v1Opts, v1Host := getPAPIConnSettings(gitpodHost, userToken, getUseCookie(), false)
121
ev1Http, ev1Opts, ev1Host := getPAPIConnSettings(gitpodHost, userToken, getUseCookie(), false)
122
123
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
124
defer cancel()
125
client := experimentalv1connect.NewProjectsServiceClient(ev1Http, ev1Host, ev1Opts...)
126
v1Client := v1connect.NewConfigurationServiceClient(v1Http, v1Host, v1Opts...)
127
resp, err := client.ListProjects(ctx, connect.NewRequest(&experimentalv1.ListProjectsRequest{
128
TeamId: orgID,
129
Pagination: &experimentalv1.Pagination{},
130
}))
131
resp2, err2 := v1Client.ListConfigurations(ctx, connect.NewRequest(&v1.ListConfigurationsRequest{
132
OrganizationId: orgID,
133
Pagination: &v1.PaginationRequest{},
134
}))
135
resp3, err3 := serverConn.GetTeamProjects(ctx, orgID)
136
137
if getUseCookie() {
138
if err != nil || err2 != nil || err3 != nil {
139
t.Errorf("should respond empty array, got: %v, %v, %v", err, err2, err3)
140
}
141
if len(resp.Msg.GetProjects()) != 0 || len(resp2.Msg.GetConfigurations()) != 0 || len(resp3) != 0 {
142
t.Errorf("should respond empty array, got: %d, %d, %d", len(resp.Msg.GetProjects()), len(resp2.Msg.GetConfigurations()), len(resp3))
143
}
144
} else {
145
if err != nil || err2 != nil {
146
t.Errorf("should respond empty array, got: %v, %v, %v", err, err2, err3)
147
}
148
149
if len(resp.Msg.GetProjects()) != 0 || len(resp2.Msg.GetConfigurations()) != 0 {
150
t.Errorf("should respond empty array, got: %d, %d", len(resp.Msg.GetProjects()), len(resp2.Msg.GetConfigurations()))
151
}
152
}
153
}
154
155
// TestGetProject tests collaborator can't access project detail with:
156
// 1. v1 + COOKIE -> not_found
157
// 2. v1 + PAT -> not_found
158
func TestGetProject(t *testing.T) {
159
if !shouldTestCollaborator() {
160
t.Skip("skipping collaborator test")
161
return
162
}
163
userToken, _ := os.LookupEnv("USER_TOKEN")
164
gitpodHost, _ := os.LookupEnv("GITPOD_HOST")
165
projectID, _ := os.LookupEnv("PROJECT_ID")
166
167
if _, err := connectToServer(gitpodHost, userToken); err != nil {
168
t.Errorf("failed getting server conn: %v", err)
169
}
170
v1Http, v1Opts, v1Host := getPAPIConnSettings(gitpodHost, userToken, getUseCookie(), false)
171
172
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
173
defer cancel()
174
175
v1Client := v1connect.NewConfigurationServiceClient(v1Http, v1Host, v1Opts...)
176
_, err2 := v1Client.GetConfiguration(ctx, connect.NewRequest(&v1.GetConfigurationRequest{
177
ConfigurationId: projectID,
178
}))
179
if !strings.Contains(err2.Error(), "not_found") {
180
t.Errorf("v1 should respond not_found, got: %s", err2)
181
}
182
}
183
184
// Note: there's no usage endpoint in the server / public v1 experimental.v1 APIs
185
186
func shouldTestCollaborator() bool {
187
should, _ := os.LookupEnv("TEST_COLLABORATOR")
188
return should == "true"
189
}
190
191
func getUseCookie() bool {
192
useCookie, _ := os.LookupEnv("USE_COOKIE")
193
return useCookie == "true"
194
}
195
196
func connectToServer(gitpodHost, token string) (*serverapi.APIoverJSONRPC, error) {
197
supervisorConn, err := grpc.Dial(util.GetSupervisorAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
198
if err != nil {
199
return nil, xerrors.Errorf("failed connecting to supervisor: %w", err)
200
}
201
defer supervisorConn.Close()
202
if err != nil {
203
return nil, xerrors.Errorf("failed getting token from supervisor: %w", err)
204
}
205
206
endpoint := "wss://" + gitpodHost + "/api/gitpod"
207
useCookie := getUseCookie()
208
opts := serverapi.ConnectToServerOpts{
209
Context: context.Background(),
210
Log: log.NewEntry(log.StandardLogger()),
211
ExtraHeaders: map[string]string{
212
"User-Agent": "gitpod/cli",
213
"X-Client-Version": "0.0.1",
214
},
215
}
216
if useCookie {
217
opts.ExtraHeaders["Cookie"] = token
218
} else {
219
opts.Token = token
220
}
221
client, err := serverapi.ConnectToServer(endpoint, opts)
222
if err != nil {
223
return nil, xerrors.Errorf("failed connecting to server: %w", err)
224
}
225
return client, nil
226
}
227
228
func getPAPIConnSettings(gitpodHost, token string, useCookie, isExperimental bool) (*http.Client, []connect.ClientOption, string) {
229
client := &http.Client{
230
Transport: &AuthenticatedTransport{Token: token, T: http.DefaultTransport, UseCookie: useCookie},
231
}
232
opts := []connect.ClientOption{
233
connect.WithInterceptors(
234
connect.UnaryInterceptorFunc(func(next connect.UnaryFunc) connect.UnaryFunc {
235
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
236
if req.Spec().IsClient {
237
if useCookie {
238
req.Header().Set("Cookie", token)
239
} else {
240
req.Header().Set("Authorization", fmt.Sprintf("Bearer %s", token))
241
}
242
}
243
return next(ctx, req)
244
}
245
}),
246
),
247
}
248
endpoint := fmt.Sprintf("https://%s/public-api", gitpodHost)
249
if isExperimental {
250
endpoint = fmt.Sprintf("https://api.%s", gitpodHost)
251
}
252
return client, opts, endpoint
253
}
254
255
type AuthenticatedTransport struct {
256
T http.RoundTripper
257
UseCookie bool
258
Token string
259
}
260
261
func (t *AuthenticatedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
262
if t.UseCookie {
263
req.Header.Add("Cookie", t.Token)
264
} else {
265
req.Header.Add("Authorization", "Bearer "+t.Token)
266
}
267
return t.T.RoundTrip(req)
268
}
269
270