Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/public-api-server/pkg/auth/middleware_test.go
2500 views
1
// Copyright (c) 2022 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 auth
6
7
import (
8
"context"
9
"fmt"
10
"net/http"
11
"net/http/httptest"
12
"testing"
13
"time"
14
15
"github.com/bufbuild/connect-go"
16
"github.com/gitpod-io/gitpod/components/public-api/go/config"
17
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
18
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws/jwstest"
19
"github.com/google/uuid"
20
"github.com/stretchr/testify/require"
21
)
22
23
func TestNewServerInterceptor(t *testing.T) {
24
requestPayload := "request"
25
type TokenResponse struct {
26
Token string `json:"token"`
27
}
28
29
type Header struct {
30
Key string
31
Value string
32
}
33
34
keyset := jwstest.GenerateKeySet(t)
35
rsa256, err := jws.NewRSA256(keyset)
36
require.NoError(t, err)
37
38
sessionCfg := config.SessionConfig{
39
Issuer: "unittest.com",
40
Cookie: config.CookieConfig{
41
Name: "cookie_jwt",
42
},
43
}
44
45
handler := connect.UnaryFunc(func(ctx context.Context, ar connect.AnyRequest) (connect.AnyResponse, error) {
46
token, _ := TokenFromContext(ctx)
47
return connect.NewResponse(&TokenResponse{Token: token.Value}), nil
48
})
49
50
validJWTToken, err := rsa256.Sign(NewSessionJWT(uuid.New(), sessionCfg.Issuer, time.Now(), time.Now().Add(5*time.Minute)))
51
require.NoError(t, err)
52
expiredJWTToken, err := rsa256.Sign(NewSessionJWT(uuid.New(), sessionCfg.Issuer, time.Now(), time.Now().Add(-1*time.Minute)))
53
require.NoError(t, err)
54
invalidIssuerJWTToken, err := rsa256.Sign(NewSessionJWT(uuid.New(), "random issuer", time.Now(), time.Now().Add(-1*time.Minute)))
55
require.NoError(t, err)
56
57
scenarios := []struct {
58
Name string
59
60
Headers []Header
61
62
ExpectedError error
63
ExpectedToken string
64
}{
65
{
66
Name: "no headers return Unathenticated",
67
Headers: nil,
68
ExpectedError: connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("No access token or cookie credentials available on request.")),
69
},
70
{
71
Name: "authorization header with bearer token returns ok",
72
Headers: []Header{{Key: "Authorization", Value: "Bearer foo"}},
73
ExpectedToken: "foo",
74
},
75
{
76
Name: "authorization header with bearer token returns ok",
77
Headers: []Header{{Key: "Authorization", Value: "Bearer foo"}},
78
ExpectedToken: "foo",
79
},
80
{
81
Name: "cookie header with invalid JWT token is rejected",
82
Headers: []Header{{Key: "Cookie", Value: fmt.Sprintf("%s=%s", sessionCfg.Cookie.Name, "invalid_token")}},
83
ExpectedError: connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("JWT session could not be verified.")),
84
},
85
{
86
Name: "cookie header with expired JWT token is rejected",
87
Headers: []Header{{
88
Key: "Cookie",
89
Value: fmt.Sprintf("%s=%s", sessionCfg.Cookie.Name, expiredJWTToken)},
90
},
91
ExpectedError: connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("JWT session could not be verified.")),
92
},
93
{
94
Name: "cookie header with invalid issuer is rejected",
95
Headers: []Header{{
96
Key: "Cookie",
97
Value: fmt.Sprintf("%s=%s", sessionCfg.Cookie.Name, invalidIssuerJWTToken)},
98
},
99
ExpectedError: connect.NewError(connect.CodeUnauthenticated, fmt.Errorf("JWT session could not be verified.")),
100
},
101
{
102
Name: "cookie header with valid JWT token is accepted",
103
Headers: []Header{{
104
Key: "Cookie",
105
Value: fmt.Sprintf("%s=%s", sessionCfg.Cookie.Name, validJWTToken),
106
}},
107
ExpectedToken: fmt.Sprintf("%s=%s", sessionCfg.Cookie.Name, validJWTToken),
108
},
109
}
110
111
for _, s := range scenarios {
112
t.Run(s.Name, func(t *testing.T) {
113
ctx := context.Background()
114
request := connect.NewRequest(&requestPayload)
115
116
for _, header := range s.Headers {
117
request.Header().Add(header.Key, header.Value)
118
}
119
120
interceptor := NewServerInterceptor(sessionCfg, rsa256)
121
resp, err := interceptor.WrapUnary(handler)(ctx, request)
122
123
require.Equal(t, s.ExpectedError, err)
124
if err == nil {
125
require.Equal(t, &TokenResponse{
126
Token: s.ExpectedToken,
127
}, resp.Any())
128
}
129
130
})
131
}
132
}
133
134
func TestNewClientInterceptor(t *testing.T) {
135
expectedToken := "my_token"
136
137
tokenOnRequest := ""
138
// Setup a test server where we capture the token supplied, we don't actually care for the response.
139
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
140
fmt.Println(r.Header)
141
token, err := BearerTokenFromHeaders(r.Header)
142
require.NoError(t, err)
143
144
// Capture the token supplied in the request so we can test for it
145
tokenOnRequest = token
146
w.WriteHeader(http.StatusNotFound)
147
}))
148
149
client := connect.NewClient[any, any](http.DefaultClient, srv.URL, connect.WithInterceptors(
150
NewClientInterceptor(expectedToken),
151
))
152
153
_, _ = client.CallUnary(context.Background(), connect.NewRequest[any](nil))
154
require.Equal(t, expectedToken, tokenOnRequest)
155
}
156
157