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.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
"errors"
9
"fmt"
10
"net/http"
11
"net/url"
12
"strconv"
13
"strings"
14
15
"github.com/gorilla/mux"
16
17
"github.com/gitpod-io/gitpod/ws-manager/api"
18
"github.com/gitpod-io/gitpod/ws-proxy/pkg/common"
19
)
20
21
var (
22
ErrTokenNotFound = fmt.Errorf("no owner cookie present")
23
ErrTokenMismatch = fmt.Errorf("owner token mismatch")
24
ErrTokenDecode = fmt.Errorf("cannot decode owner token")
25
)
26
27
// WorkspaceAuthHandler rejects requests which are not authenticated or authorized to access a workspace.
28
func WorkspaceAuthHandler(domain string, info common.WorkspaceInfoProvider) mux.MiddlewareFunc {
29
return func(h http.Handler) http.Handler {
30
cookiePrefix := domain
31
for _, c := range []string{" ", "-", "."} {
32
cookiePrefix = strings.ReplaceAll(cookiePrefix, c, "_")
33
}
34
cookiePrefix = "_" + cookiePrefix + "_ws_"
35
36
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
37
var (
38
log = getLog(req.Context())
39
vars = mux.Vars(req)
40
wsID = vars[common.WorkspaceIDIdentifier]
41
port = vars[common.WorkspacePortIdentifier]
42
)
43
if wsID == "" {
44
log.Warn("workspace request without workspace ID")
45
resp.WriteHeader(http.StatusForbidden)
46
47
return
48
}
49
50
ws := info.WorkspaceInfo(wsID)
51
if ws == nil {
52
resp.WriteHeader(http.StatusNotFound)
53
54
return
55
}
56
57
isPublic := false
58
if ws.Auth != nil && ws.Auth.Admission == api.AdmissionLevel_ADMIT_EVERYONE {
59
isPublic = true
60
} else if port != "" {
61
prt, err := strconv.ParseUint(port, 10, 16)
62
if err != nil {
63
log.WithField("port", port).WithError(err).Error("cannot convert port to int")
64
} else {
65
for _, p := range ws.Ports {
66
if p.Port == uint32(prt) {
67
isPublic = p.Visibility == api.PortVisibility_PORT_VISIBILITY_PUBLIC
68
break
69
}
70
}
71
}
72
}
73
74
authenticate := func() (bool, error) {
75
tkn := req.Header.Get("x-gitpod-owner-token")
76
if tkn == "" {
77
cn := fmt.Sprintf("%s%s_owner_", cookiePrefix, ws.InstanceID)
78
c, err := req.Cookie(cn)
79
if err != nil {
80
return false, ErrTokenNotFound
81
}
82
tkn = c.Value
83
}
84
tkn, err := url.QueryUnescape(tkn)
85
if err != nil {
86
return false, ErrTokenDecode
87
}
88
89
if tkn != ws.Auth.OwnerToken {
90
return false, ErrTokenMismatch
91
}
92
return true, nil
93
}
94
95
authenticated, err := authenticate()
96
if !authenticated && !isPublic {
97
if err != nil {
98
if errors.Is(err, ErrTokenNotFound) {
99
resp.WriteHeader(http.StatusUnauthorized)
100
return
101
}
102
if errors.Is(err, ErrTokenMismatch) {
103
log.Warn("owner token mismatch")
104
resp.WriteHeader(http.StatusForbidden)
105
return
106
}
107
if errors.Is(err, ErrTokenDecode) {
108
log.Warn("cannot decode owner token")
109
resp.WriteHeader(http.StatusBadRequest)
110
return
111
}
112
}
113
log.WithError(err).Error("cannot authenticate")
114
resp.WriteHeader(http.StatusInternalServerError)
115
return
116
}
117
118
if !authenticated && isPublic {
119
ctx, id, err := info.AcquireContext(req.Context(), wsID, port)
120
if err != nil {
121
log.WithError(err).Error("cannot acquire context")
122
resp.WriteHeader(http.StatusInternalServerError)
123
return
124
}
125
defer info.ReleaseContext(id)
126
req = req.WithContext(ctx)
127
}
128
129
h.ServeHTTP(resp, req)
130
})
131
}
132
}
133
134