mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
chore: add workspace proxies to the backend (#7032)
Co-authored-by: Dean Sheather <dean@deansheather.com>
This commit is contained in:
@ -3,6 +3,7 @@ package coderd_test
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
@ -10,8 +11,13 @@ import (
|
||||
|
||||
"github.com/coder/coder/cli/clibase"
|
||||
"github.com/coder/coder/coderd/coderdtest"
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/database/dbgen"
|
||||
"github.com/coder/coder/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/coderd/httpmw"
|
||||
"github.com/coder/coder/coderd/workspaceapps"
|
||||
"github.com/coder/coder/coderd/workspaceapps/apptest"
|
||||
"github.com/coder/coder/codersdk"
|
||||
"github.com/coder/coder/testutil"
|
||||
)
|
||||
|
||||
@ -78,6 +84,171 @@ func TestGetAppHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkspaceApplicationAuth(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
accessURL string
|
||||
appHostname string
|
||||
proxyURL string
|
||||
proxyAppHostname string
|
||||
|
||||
redirectURI string
|
||||
expectRedirect string
|
||||
}{
|
||||
{
|
||||
name: "OK",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "https://something.test.coder.com",
|
||||
expectRedirect: "https://something.test.coder.com",
|
||||
},
|
||||
{
|
||||
name: "ProxyPathOK",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "https://proxy.test.coder.com/path",
|
||||
expectRedirect: "https://proxy.test.coder.com/path",
|
||||
},
|
||||
{
|
||||
name: "ProxySubdomainOK",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "https://something.proxy.test.coder.com/path?yeah=true",
|
||||
expectRedirect: "https://something.proxy.test.coder.com/path?yeah=true",
|
||||
},
|
||||
{
|
||||
name: "ProxySubdomainSuffixOK",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*--suffix.proxy.test.coder.com",
|
||||
redirectURI: "https://something--suffix.proxy.test.coder.com/",
|
||||
expectRedirect: "https://something--suffix.proxy.test.coder.com/",
|
||||
},
|
||||
{
|
||||
name: "NormalizeSchemePrimaryAppHostname",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "http://x.test.coder.com",
|
||||
expectRedirect: "https://x.test.coder.com",
|
||||
},
|
||||
{
|
||||
name: "NormalizeSchemeProxyAppHostname",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "http://x.proxy.test.coder.com",
|
||||
expectRedirect: "https://x.proxy.test.coder.com",
|
||||
},
|
||||
{
|
||||
name: "NoneError",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "",
|
||||
expectRedirect: "",
|
||||
},
|
||||
{
|
||||
name: "PrimaryAccessURLError",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "https://test.coder.com/",
|
||||
expectRedirect: "",
|
||||
},
|
||||
{
|
||||
name: "OtherError",
|
||||
accessURL: "https://test.coder.com",
|
||||
appHostname: "*.test.coder.com",
|
||||
proxyURL: "https://proxy.test.coder.com",
|
||||
proxyAppHostname: "*.proxy.test.coder.com",
|
||||
redirectURI: "https://example.com/",
|
||||
expectRedirect: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, pubsub := dbtestutil.NewDB(t)
|
||||
|
||||
accessURL, err := url.Parse(c.accessURL)
|
||||
require.NoError(t, err)
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
Database: db,
|
||||
Pubsub: pubsub,
|
||||
AccessURL: accessURL,
|
||||
AppHostname: c.appHostname,
|
||||
})
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
// Disable redirects.
|
||||
client.HTTPClient.CheckRedirect = func(_ *http.Request, _ []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
_, _ = dbgen.WorkspaceProxy(t, db, database.WorkspaceProxy{
|
||||
Url: c.proxyURL,
|
||||
WildcardHostname: c.proxyAppHostname,
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.Request(ctx, http.MethodGet, "/api/v2/applications/auth-redirect", nil, func(req *http.Request) {
|
||||
q := req.URL.Query()
|
||||
q.Set("redirect_uri", c.redirectURI)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
})
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusSeeOther {
|
||||
err = codersdk.ReadBodyAsError(resp)
|
||||
if c.expectRedirect == "" {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
return
|
||||
}
|
||||
if c.expectRedirect == "" {
|
||||
t.Fatal("expected a failure but got a success")
|
||||
}
|
||||
|
||||
loc, err := resp.Location()
|
||||
require.NoError(t, err)
|
||||
q := loc.Query()
|
||||
|
||||
// Verify the API key is set.
|
||||
encryptedAPIKey := loc.Query().Get(workspaceapps.SubdomainProxyAPIKeyParam)
|
||||
require.NotEmpty(t, encryptedAPIKey, "no API key was set in the query parameters")
|
||||
|
||||
// Strip the API key from the actual redirect URI and compare.
|
||||
q.Del(workspaceapps.SubdomainProxyAPIKeyParam)
|
||||
loc.RawQuery = q.Encode()
|
||||
require.Equal(t, c.expectRedirect, loc.String())
|
||||
|
||||
// The decrypted key is verified in the apptest test suite.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkspaceApps(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@ -87,6 +258,10 @@ func TestWorkspaceApps(t *testing.T) {
|
||||
deploymentValues.Dangerous.AllowPathAppSharing = clibase.Bool(opts.DangerousAllowPathAppSharing)
|
||||
deploymentValues.Dangerous.AllowPathAppSiteOwnerAccess = clibase.Bool(opts.DangerousAllowPathAppSiteOwnerAccess)
|
||||
|
||||
if opts.DisableSubdomainApps {
|
||||
opts.AppHost = ""
|
||||
}
|
||||
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
DeploymentValues: deploymentValues,
|
||||
AppHostname: opts.AppHost,
|
||||
@ -105,10 +280,11 @@ func TestWorkspaceApps(t *testing.T) {
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
return &apptest.Deployment{
|
||||
Options: opts,
|
||||
Client: client,
|
||||
FirstUser: user,
|
||||
PathAppBaseURL: client.URL,
|
||||
Options: opts,
|
||||
SDKClient: client,
|
||||
FirstUser: user,
|
||||
PathAppBaseURL: client.URL,
|
||||
AppHostIsPrimary: true,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user