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