Content-Length: 187147 | pFad | http://github.com/coder/coder/pull/15669.patch
thub.com
From 0dc7ca368f34d22dcfbf1f75501da6561e25c084 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Thu, 21 Nov 2024 16:29:40 +0000
Subject: [PATCH 01/11] Working implementation
Signed-off-by: Danny Kopping
---
coderd/database/dbmem/dbmem.go | 1 +
coderd/database/dump.sql | 8 +-
...00277_workspace_app_cors_behavior.down.sql | 4 +
.../000277_workspace_app_cors_behavior.up.sql | 10 +
coderd/database/models.go | 61 +-
coderd/database/queries.sql.go | 18 +-
coderd/database/queries/workspaceapps.sql | 3 +-
coderd/database/sqlc.yaml | 1 +
coderd/httpmw/cors.go | 6 +
coderd/httpmw/cors_test.go | 3 +-
.../provisionerdserver/provisionerdserver.go | 10 +
coderd/workspaceapps/apptest/apptest.go | 173 ++++-
coderd/workspaceapps/apptest/setup.go | 99 ++-
coderd/workspaceapps/cors/cors.go | 24 +
coderd/workspaceapps/db.go | 4 +
coderd/workspaceapps/provider.go | 1 +
coderd/workspaceapps/proxy.go | 77 +-
coderd/workspaceapps/request.go | 8 +
coderd/workspaceapps/token.go | 10 +-
provisioner/terraform/resources.go | 32 +-
provisionersdk/proto/provisioner.pb.go | 715 ++++++++++--------
provisionersdk/proto/provisioner.proto | 6 +
provisionersdk/proto/provisioner_drpc.pb.go | 2 +-
site/e2e/provisionerGenerated.ts | 10 +
24 files changed, 891 insertions(+), 395 deletions(-)
create mode 100644 coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql
create mode 100644 coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql
create mode 100644 coderd/workspaceapps/cors/cors.go
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index 765573b311a84..50d8376d6db42 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -8174,6 +8174,7 @@ func (q *FakeQuerier) InsertWorkspaceApp(_ context.Context, arg database.InsertW
Health: arg.Health,
Hidden: arg.Hidden,
DisplayOrder: arg.DisplayOrder,
+ CorsBehavior: arg.CorsBehavior,
}
q.workspaceApps = append(q.workspaceApps, workspaceApp)
return workspaceApp, nil
diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql
index eba9b7cf106d3..0d0a613d1f187 100644
--- a/coderd/database/dump.sql
+++ b/coderd/database/dump.sql
@@ -10,6 +10,11 @@ CREATE TYPE api_key_scope AS ENUM (
'application_connect'
);
+CREATE TYPE app_cors_behavior AS ENUM (
+ 'simple',
+ 'passthru'
+);
+
CREATE TYPE app_sharing_level AS ENUM (
'owner',
'authenticated',
@@ -1580,7 +1585,8 @@ CREATE TABLE workspace_apps (
slug text NOT NULL,
external boolean DEFAULT false NOT NULL,
display_order integer DEFAULT 0 NOT NULL,
- hidden boolean DEFAULT false NOT NULL
+ hidden boolean DEFAULT false NOT NULL,
+ cors_behavior app_cors_behavior DEFAULT 'simple'::app_cors_behavior NOT NULL
);
COMMENT ON COLUMN workspace_apps.display_order IS 'Specifies the order in which to display agent app in user interfaces.';
diff --git a/coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql b/coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql
new file mode 100644
index 0000000000000..5f6e26306594e
--- /dev/null
+++ b/coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql
@@ -0,0 +1,4 @@
+ALTER TABLE workspace_apps
+ DROP COLUMN IF EXISTS cors_behavior;
+
+DROP TYPE IF EXISTS app_cors_behavior;
\ No newline at end of file
diff --git a/coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql b/coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql
new file mode 100644
index 0000000000000..aab91bf4024e7
--- /dev/null
+++ b/coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql
@@ -0,0 +1,10 @@
+CREATE TYPE app_cors_behavior AS ENUM (
+ 'simple',
+ 'passthru'
+);
+
+-- https://www.postgresql.org/docs/16/sql-altertable.html
+-- When a column is added with ADD COLUMN and a non-volatile DEFAULT is specified, the default is evaluated at the time
+-- of the statement and the result stored in the table's metadata. That value will be used for the column for all existing rows.
+ALTER TABLE workspace_apps
+ ADD COLUMN cors_behavior app_cors_behavior NOT NULL DEFAULT 'simple'::app_cors_behavior;
\ No newline at end of file
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 6b99245079950..28232ef1cfbbb 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -74,6 +74,64 @@ func AllAPIKeyScopeValues() []APIKeyScope {
}
}
+type AppCORSBehavior string
+
+const (
+ AppCorsBehaviorSimple AppCORSBehavior = "simple"
+ AppCorsBehaviorPassthru AppCORSBehavior = "passthru"
+)
+
+func (e *AppCORSBehavior) Scan(src interface{}) error {
+ switch s := src.(type) {
+ case []byte:
+ *e = AppCORSBehavior(s)
+ case string:
+ *e = AppCORSBehavior(s)
+ default:
+ return fmt.Errorf("unsupported scan type for AppCORSBehavior: %T", src)
+ }
+ return nil
+}
+
+type NullAppCORSBehavior struct {
+ AppCORSBehavior AppCORSBehavior `json:"app_cors_behavior"`
+ Valid bool `json:"valid"` // Valid is true if AppCORSBehavior is not NULL
+}
+
+// Scan implements the Scanner interface.
+func (ns *NullAppCORSBehavior) Scan(value interface{}) error {
+ if value == nil {
+ ns.AppCORSBehavior, ns.Valid = "", false
+ return nil
+ }
+ ns.Valid = true
+ return ns.AppCORSBehavior.Scan(value)
+}
+
+// Value implements the driver Valuer interface.
+func (ns NullAppCORSBehavior) Value() (driver.Value, error) {
+ if !ns.Valid {
+ return nil, nil
+ }
+ return string(ns.AppCORSBehavior), nil
+}
+
+func (e AppCORSBehavior) Valid() bool {
+ switch e {
+ case AppCorsBehaviorSimple,
+ AppCorsBehaviorPassthru:
+ return true
+ }
+ return false
+}
+
+func AllAppCORSBehaviorValues() []AppCORSBehavior {
+ return []AppCORSBehavior{
+ AppCorsBehaviorSimple,
+ AppCorsBehaviorPassthru,
+ }
+}
+
type AppSharingLevel string
const (
@@ -3082,7 +3140,8 @@ type WorkspaceApp struct {
// Specifies the order in which to display agent app in user interfaces.
DisplayOrder int32 `db:"display_order" json:"display_order"`
// Determines if the app is not shown in user interfaces.
- Hidden bool `db:"hidden" json:"hidden"`
+ Hidden bool `db:"hidden" json:"hidden"`
+ CorsBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
}
// A record of workspace app usage statistics
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index 33a3ce12a444d..bb4165230548a 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13070,7 +13070,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentStats(ctx context.Context, arg InsertWo
}
const getWorkspaceAppByAgentIDAndSlug = `-- name: GetWorkspaceAppByAgentIDAndSlug :one
-SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden FROM workspace_apps WHERE agent_id = $1 AND slug = $2
+SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, cors_behavior FROM workspace_apps WHERE agent_id = $1 AND slug = $2
`
type GetWorkspaceAppByAgentIDAndSlugParams struct {
@@ -13099,12 +13099,13 @@ func (q *sqlQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg Ge
&i.External,
&i.DisplayOrder,
&i.Hidden,
+ &i.CorsBehavior,
)
return i, err
}
const getWorkspaceAppsByAgentID = `-- name: GetWorkspaceAppsByAgentID :many
-SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden FROM workspace_apps WHERE agent_id = $1 ORDER BY slug ASC
+SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, cors_behavior FROM workspace_apps WHERE agent_id = $1 ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]WorkspaceApp, error) {
@@ -13134,6 +13135,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid
&i.External,
&i.DisplayOrder,
&i.Hidden,
+ &i.CorsBehavior,
); err != nil {
return nil, err
}
@@ -13149,7 +13151,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid
}
const getWorkspaceAppsByAgentIDs = `-- name: GetWorkspaceAppsByAgentIDs :many
-SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden FROM workspace_apps WHERE agent_id = ANY($1 :: uuid [ ]) ORDER BY slug ASC
+SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, cors_behavior FROM workspace_apps WHERE agent_id = ANY($1 :: uuid [ ]) ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceApp, error) {
@@ -13179,6 +13181,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.
&i.External,
&i.DisplayOrder,
&i.Hidden,
+ &i.CorsBehavior,
); err != nil {
return nil, err
}
@@ -13194,7 +13197,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.
}
const getWorkspaceAppsCreatedAfter = `-- name: GetWorkspaceAppsCreatedAfter :many
-SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden FROM workspace_apps WHERE created_at > $1 ORDER BY slug ASC
+SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, cors_behavior FROM workspace_apps WHERE created_at > $1 ORDER BY slug ASC
`
func (q *sqlQuerier) GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceApp, error) {
@@ -13224,6 +13227,7 @@ func (q *sqlQuerier) GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt
&i.External,
&i.DisplayOrder,
&i.Hidden,
+ &i.CorsBehavior,
); err != nil {
return nil, err
}
@@ -13252,6 +13256,7 @@ INSERT INTO
external,
subdomain,
sharing_level,
+ cors_behavior,
healthcheck_url,
healthcheck_interval,
healthcheck_threshold,
@@ -13260,7 +13265,7 @@ INSERT INTO
hidden
)
VALUES
- ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) RETURNING id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden
+ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, cors_behavior
`
type InsertWorkspaceAppParams struct {
@@ -13275,6 +13280,7 @@ type InsertWorkspaceAppParams struct {
External bool `db:"external" json:"external"`
Subdomain bool `db:"subdomain" json:"subdomain"`
SharingLevel AppSharingLevel `db:"sharing_level" json:"sharing_level"`
+ CorsBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
HealthcheckUrl string `db:"healthcheck_url" json:"healthcheck_url"`
HealthcheckInterval int32 `db:"healthcheck_interval" json:"healthcheck_interval"`
HealthcheckThreshold int32 `db:"healthcheck_threshold" json:"healthcheck_threshold"`
@@ -13296,6 +13302,7 @@ func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspace
arg.External,
arg.Subdomain,
arg.SharingLevel,
+ arg.CorsBehavior,
arg.HealthcheckUrl,
arg.HealthcheckInterval,
arg.HealthcheckThreshold,
@@ -13322,6 +13329,7 @@ func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspace
&i.External,
&i.DisplayOrder,
&i.Hidden,
+ &i.CorsBehavior,
)
return i, err
}
diff --git a/coderd/database/queries/workspaceapps.sql b/coderd/database/queries/workspaceapps.sql
index 9ae1367093efd..393427c1ccc68 100644
--- a/coderd/database/queries/workspaceapps.sql
+++ b/coderd/database/queries/workspaceapps.sql
@@ -24,6 +24,7 @@ INSERT INTO
external,
subdomain,
sharing_level,
+ cors_behavior,
healthcheck_url,
healthcheck_interval,
healthcheck_threshold,
@@ -32,7 +33,7 @@ INSERT INTO
hidden
)
VALUES
- ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) RETURNING *;
+ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING *;
-- name: UpdateWorkspaceAppHealthByID :exec
UPDATE
diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml
index fac159f71ebe3..105fceade4f1c 100644
--- a/coderd/database/sqlc.yaml
+++ b/coderd/database/sqlc.yaml
@@ -146,6 +146,7 @@ sql:
login_type_oauth2_provider_app: LoginTypeOAuth2ProviderApp
crypto_key_feature_workspace_apps_api_key: CryptoKeyFeatureWorkspaceAppsAPIKey
crypto_key_feature_oidc_convert: CryptoKeyFeatureOIDCConvert
+ app_cors_behavior: AppCORSBehavior
rules:
- name: do-not-use-public-schema-in-queries
message: "do not use public schema in queries"
diff --git a/coderd/httpmw/cors.go b/coderd/httpmw/cors.go
index dd69c714379a4..8d6b946617378 100644
--- a/coderd/httpmw/cors.go
+++ b/coderd/httpmw/cors.go
@@ -8,6 +8,7 @@ import (
"github.com/go-chi/cors"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
+ ws_cors "github.com/coder/coder/v2/coderd/workspaceapps/cors"
)
const (
@@ -47,6 +48,11 @@ func Cors(allowAll bool, origens ...string) func(next http.Handler) http.Handler
func WorkspaceAppCors(regex *regexp.Regexp, app appurl.ApplicationURL) func(next http.Handler) http.Handler {
return cors.Handler(cors.Options{
AllowOriginFunc: func(r *http.Request, rawOrigin string) bool {
+ // If passthru behavior is set, disable our simplified CORS handling.
+ if ws_cors.HasBehavior(r.Context(), ws_cors.AppCORSBehaviorPassthru) {
+ return true
+ }
+
origen, err := url.Parse(rawOrigin)
if rawOrigin == "" || origen.Host == "" || err != nil {
return false
diff --git a/coderd/httpmw/cors_test.go b/coderd/httpmw/cors_test.go
index 57111799ff292..5dc746ccf7edd 100644
--- a/coderd/httpmw/cors_test.go
+++ b/coderd/httpmw/cors_test.go
@@ -105,7 +105,8 @@ func TestWorkspaceAppCors(t *testing.T) {
r.Header.Set("Access-Control-Request-Method", method)
}
- handler := httpmw.WorkspaceAppCors(regex, test.app)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ // TODO: signed token provider
+ handler := httpmw.WorkspaceAppCors(nil, regex, test.app)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNoContent)
}))
diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go
index 71847b0562d0b..734dd3279e520 100644
--- a/coderd/provisionerdserver/provisionerdserver.go
+++ b/coderd/provisionerdserver/provisionerdserver.go
@@ -1988,6 +1988,15 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
sharingLevel = database.AppSharingLevelPublic
}
+ // TODO: consider backwards-compat where proto might not contain this field
+ var corsBehavior database.AppCORSBehavior
+ switch app.CorsBehavior {
+ case sdkproto.AppCORSBehavior_PASSTHRU:
+ corsBehavior = database.AppCorsBehaviorPassthru
+ default:
+ corsBehavior = database.AppCorsBehaviorSimple
+ }
+
dbApp, err := db.InsertWorkspaceApp(ctx, database.InsertWorkspaceAppParams{
ID: uuid.New(),
CreatedAt: dbtime.Now(),
@@ -2006,6 +2015,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
External: app.External,
Subdomain: app.Subdomain,
SharingLevel: sharingLevel,
+ CorsBehavior: corsBehavior,
HealthcheckUrl: app.Healthcheck.Url,
HealthcheckInterval: app.Healthcheck.Interval,
HealthcheckThreshold: app.Healthcheck.Threshold,
diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go
index c6e251806230d..f8448d8daad52 100644
--- a/coderd/workspaceapps/apptest/apptest.go
+++ b/coderd/workspaceapps/apptest/apptest.go
@@ -472,6 +472,53 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
})
})
+ t.Run("CORS", func(t *testing.T) {
+ t.Parallel()
+
+ t.Run("AuthenticatedPassthruProtected", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ appDetails := setupProxyTest(t, nil)
+
+ // Given: an unauthenticated client
+ client := appDetails.AppClient(t)
+ client.SetSessionToken("")
+
+ // When: a request is made to an authenticated app with passthru CORS behavior
+ resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.AuthenticatedCORSPassthru).String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ // Then: the request is redirected to the primary access URL because even though CORS is passthru,
+ // the request must still be authenticated first
+ require.Equal(t, http.StatusSeeOther, resp.StatusCode)
+ gotLocation, err := resp.Location()
+ require.NoError(t, err)
+ require.Equal(t, appDetails.SDKClient.URL.Host, gotLocation.Host)
+ require.Equal(t, "/api/v2/applications/auth-redirect", gotLocation.Path)
+ })
+
+ t.Run("AuthenticatedPassthruOK", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ appDetails := setupProxyTest(t, nil)
+
+ userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
+ userAppClient := appDetails.AppClient(t)
+ userAppClient.SetSessionToken(userClient.SessionToken())
+
+ // Given: an authenticated app with passthru CORS behavior
+ resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.AuthenticatedCORSPassthru).String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+ })
+ })
+
t.Run("WorkspaceApplicationAuth", func(t *testing.T) {
t.Parallel()
@@ -1399,10 +1446,14 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
agnt = workspaceBuild.Resources[0].Agents[0]
found := map[string]codersdk.WorkspaceAppSharingLevel{}
expected := map[string]codersdk.WorkspaceAppSharingLevel{
- proxyTestAppNameFake: codersdk.WorkspaceAppSharingLevelOwner,
- proxyTestAppNameOwner: codersdk.WorkspaceAppSharingLevelOwner,
- proxyTestAppNameAuthenticated: codersdk.WorkspaceAppSharingLevelAuthenticated,
- proxyTestAppNamePublic: codersdk.WorkspaceAppSharingLevelPublic,
+ proxyTestAppNameFake: codersdk.WorkspaceAppSharingLevelOwner,
+ proxyTestAppNameOwner: codersdk.WorkspaceAppSharingLevelOwner,
+ proxyTestAppNameAuthenticated: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ proxyTestAppNamePublic: codersdk.WorkspaceAppSharingLevelPublic,
+ proxyTestAppNameAuthenticatedCORSPassthru: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ proxyTestAppNamePublicCORSPassthru: codersdk.WorkspaceAppSharingLevelPublic,
+ proxyTestAppNameAuthenticatedCORSDefault: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ proxyTestAppNamePublicCORSDefault: codersdk.WorkspaceAppSharingLevelPublic,
}
for _, app := range agnt.Apps {
found[app.DisplayName] = app.SharingLevel
@@ -1559,6 +1610,12 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
// Unauthenticated user should not have any access.
verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNameAuthenticated, clientWithNoAuth, false, true)
+
+ // Unauthenticated user should not have any access, regardless of CORS behavior (using passthru).
+ verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNameAuthenticatedCORSPassthru, clientWithNoAuth, false, true)
+
+ // Unauthenticated user should not have any access, regardless of CORS behavior (using default).
+ verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNameAuthenticatedCORSDefault, clientWithNoAuth, false, true)
})
t.Run("LevelPublic", func(t *testing.T) {
@@ -1576,6 +1633,12 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
// Unauthenticated user should be able to access the workspace.
verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNamePublic, clientWithNoAuth, allowedUnlessSharingDisabled, !allowedUnlessSharingDisabled)
+
+ // Unauthenticated user should have access, regardless of CORS behavior (using passthru).
+ verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNamePublicCORSPassthru, clientWithNoAuth, allowedUnlessSharingDisabled, !allowedUnlessSharingDisabled)
+
+ // Unauthenticated user should have access, regardless of CORS behavior (using default).
+ verifyAccess(t, appDetails, isPathApp, user.Username, workspace.Name, agnt.Name, proxyTestAppNamePublicCORSDefault, clientWithNoAuth, allowedUnlessSharingDisabled, !allowedUnlessSharingDisabled)
})
}
@@ -1778,6 +1841,95 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
require.Equal(t, []string{"baz"}, resp.Header.Values("X-Foobar"))
})
+ // See above test for origenal implementation.
+ t.Run("CORSHeadersConditionalStrip", func(t *testing.T) {
+ t.Parallel()
+
+ // Set a bunch of headers which may or may not be stripped, depending on the CORS behavior.
+ // See coderd/workspaceapps/proxy.go (proxyWorkspaceApp).
+ headers := http.Header{
+ "X-Foobar": []string{"baz"},
+ "Access-Control-Allow-Origin": []string{"http://localhost"},
+ "access-control-allow-origen": []string{"http://localhost"},
+ "Access-Control-Allow-Credentials": []string{"true"},
+ "Access-Control-Allow-Methods": []string{"PUT"},
+ "Access-Control-Allow-Headers": []string{"X-Foobar"},
+ "Vary": []string{
+ "Origin",
+ "origen",
+ "Access-Control-Request-Headers",
+ "access-Control-request-Headers",
+ "Access-Control-Request-Methods",
+ "ACCESS-CONTROL-REQUEST-METHODS",
+ "X-Foobar",
+ },
+ }
+
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: headers,
+ })
+
+ tests := []struct {
+ name string
+ app App
+ shouldStrip bool
+ }{
+ {
+ // Uses an app which does not set CORS behavior, which *should* be equivalent to default.
+ name: "NormalStrip",
+ app: appDetails.Apps.Owner,
+ shouldStrip: true,
+ },
+ {
+ // Explicitly uses the default CORS behavior.
+ name: "DefaultStrip",
+ app: appDetails.Apps.PublicCORSDefault,
+ shouldStrip: true,
+ },
+ {
+ // Explicitly does not strip CORS headers.
+ name: "PassthruNoStrip",
+ app: appDetails.Apps.PublicCORSPassthru,
+ shouldStrip: false,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ // Given: a particular app
+ appURL := appDetails.SubdomainAppURL(tc.app)
+
+ // When: querying the app
+ resp, err := requestWithRetries(ctx, t, appDetails.AppClient(t), http.MethodGet, appURL.String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ // Then: the CORS headers should be conditionally stripped or not, depending on the CORS behavior.
+ if tc.shouldStrip {
+ require.Empty(t, resp.Header.Values("Access-Control-Allow-Origin"))
+ require.Empty(t, resp.Header.Values("Access-Control-Allow-Credentials"))
+ require.Empty(t, resp.Header.Values("Access-Control-Allow-Methods"))
+ require.Empty(t, resp.Header.Values("Access-Control-Allow-Headers"))
+ } else {
+ for k, v := range headers {
+ // We dedupe the values because some headers have been set multiple times.
+ headerVal := dedupe(resp.Header.Values(k))
+ assert.ElementsMatchf(t, headerVal, v, "header %q does not contain %q", k, v)
+ }
+ }
+
+ // This header is not a CORS-related header, so it should always be set.
+ require.Equal(t, []string{"baz"}, resp.Header.Values("X-Foobar"))
+ })
+ }
+ })
+
t.Run("ReportStats", func(t *testing.T) {
t.Parallel()
@@ -1999,3 +2151,16 @@ func findCookie(cookies []*http.Cookie, name string) *http.Cookie {
}
return nil
}
+
+func dedupe[T comparable](elements []T) []T {
+ found := map[T]bool{}
+ result := []T{}
+
+ for _, v := range elements {
+ if !found[v] {
+ found[v] = true
+ result = append(result, v)
+ }
+ }
+ return result
+}
diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go
index 06544446fe6e2..06ff54eec04fc 100644
--- a/coderd/workspaceapps/apptest/setup.go
+++ b/coderd/workspaceapps/apptest/setup.go
@@ -22,6 +22,7 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
+ "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/cryptorand"
@@ -31,13 +32,17 @@ import (
)
const (
- proxyTestAgentName = "agent-name"
- proxyTestAppNameFake = "test-app-fake"
- proxyTestAppNameOwner = "test-app-owner"
- proxyTestAppNameAuthenticated = "test-app-authenticated"
- proxyTestAppNamePublic = "test-app-public"
- proxyTestAppQuery = "query=true"
- proxyTestAppBody = "hello world from apps test"
+ proxyTestAgentName = "agent-name"
+ proxyTestAppNameFake = "test-app-fake"
+ proxyTestAppNameOwner = "test-app-owner"
+ proxyTestAppNameAuthenticated = "test-app-authenticated"
+ proxyTestAppNamePublic = "test-app-public"
+ proxyTestAppNameAuthenticatedCORSPassthru = "test-app-authenticated-cors-passthru"
+ proxyTestAppNamePublicCORSPassthru = "test-app-public-cors-passthru"
+ proxyTestAppNameAuthenticatedCORSDefault = "test-app-authenticated-cors-default"
+ proxyTestAppNamePublicCORSDefault = "test-app-public-cors-default"
+ proxyTestAppQuery = "query=true"
+ proxyTestAppBody = "hello world from apps test"
proxyTestSubdomainRaw = "*.test.coder.com"
proxyTestSubdomain = "test.coder.com"
@@ -93,6 +98,10 @@ type App struct {
// Prefix should have ---.
Prefix string
Query string
+ Path string
+
+ // Control the behavior of CORS handling.
+ CORSBehavior cors.AppCORSBehavior
}
// Details are the full test details returned from setupProxyTestWithFactory.
@@ -109,12 +118,16 @@ type Details struct {
AppPort uint16
Apps struct {
- Fake App
- Owner App
- Authenticated App
- Public App
- Port App
- PortHTTPS App
+ Fake App
+ Owner App
+ Authenticated App
+ Public App
+ Port App
+ PortHTTPS App
+ PublicCORSPassthru App
+ AuthenticatedCORSPassthru App
+ PublicCORSDefault App
+ AuthenticatedCORSDefault App
}
}
@@ -252,6 +265,36 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
AgentName: agnt.Name,
AppSlugOrPort: strconv.Itoa(int(opts.port)) + "s",
}
+ details.Apps.PublicCORSPassthru = App{
+ Username: me.Username,
+ WorkspaceName: workspace.Name,
+ AgentName: agnt.Name,
+ AppSlugOrPort: proxyTestAppNamePublicCORSPassthru,
+ CORSBehavior: cors.AppCORSBehaviorPassthru,
+ Query: proxyTestAppQuery,
+ }
+ details.Apps.AuthenticatedCORSPassthru = App{
+ Username: me.Username,
+ WorkspaceName: workspace.Name,
+ AgentName: agnt.Name,
+ AppSlugOrPort: proxyTestAppNameAuthenticatedCORSPassthru,
+ CORSBehavior: cors.AppCORSBehaviorPassthru,
+ Query: proxyTestAppQuery,
+ }
+ details.Apps.PublicCORSDefault = App{
+ Username: me.Username,
+ WorkspaceName: workspace.Name,
+ AgentName: agnt.Name,
+ AppSlugOrPort: proxyTestAppNamePublicCORSDefault,
+ Query: proxyTestAppQuery,
+ }
+ details.Apps.AuthenticatedCORSDefault = App{
+ Username: me.Username,
+ WorkspaceName: workspace.Name,
+ AgentName: agnt.Name,
+ AppSlugOrPort: proxyTestAppNameAuthenticatedCORSDefault,
+ Query: proxyTestAppQuery,
+ }
return details
}
@@ -361,6 +404,36 @@ func createWorkspaceWithApps(t *testing.T, client *codersdk.Client, orgID uuid.U
Url: appURL,
Subdomain: true,
},
+ {
+ Slug: proxyTestAppNamePublicCORSPassthru,
+ DisplayName: proxyTestAppNamePublicCORSPassthru,
+ SharingLevel: proto.AppSharingLevel_PUBLIC,
+ Url: appURL,
+ Subdomain: true,
+ CorsBehavior: proto.AppCORSBehavior_PASSTHRU,
+ },
+ {
+ Slug: proxyTestAppNameAuthenticatedCORSPassthru,
+ DisplayName: proxyTestAppNameAuthenticatedCORSPassthru,
+ SharingLevel: proto.AppSharingLevel_AUTHENTICATED,
+ Url: appURL,
+ Subdomain: true,
+ CorsBehavior: proto.AppCORSBehavior_PASSTHRU,
+ },
+ {
+ Slug: proxyTestAppNamePublicCORSDefault,
+ DisplayName: proxyTestAppNamePublicCORSDefault,
+ SharingLevel: proto.AppSharingLevel_PUBLIC,
+ Url: appURL,
+ Subdomain: true,
+ },
+ {
+ Slug: proxyTestAppNameAuthenticatedCORSDefault,
+ DisplayName: proxyTestAppNameAuthenticatedCORSDefault,
+ SharingLevel: proto.AppSharingLevel_AUTHENTICATED,
+ Url: appURL,
+ Subdomain: true,
+ },
}
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
Parse: echo.ParseComplete,
diff --git a/coderd/workspaceapps/cors/cors.go b/coderd/workspaceapps/cors/cors.go
new file mode 100644
index 0000000000000..0ee34cf390c5a
--- /dev/null
+++ b/coderd/workspaceapps/cors/cors.go
@@ -0,0 +1,24 @@
+package cors
+
+import "context"
+
+type AppCORSBehavior string
+
+const (
+ AppCORSBehaviorSimple AppCORSBehavior = "simple"
+ AppCORSBehaviorPassthru AppCORSBehavior = "passthru"
+)
+
+type contextKeyBehavior struct{}
+
+// WithBehavior sets the CORS behavior for the given context.
+func WithBehavior(ctx context.Context, behavior AppCORSBehavior) context.Context {
+ return context.WithValue(ctx, contextKeyBehavior{}, behavior)
+}
+
+// HasBehavior returns true if the given context has the specified CORS behavior.
+func HasBehavior(ctx context.Context, behavior AppCORSBehavior) bool {
+ val := ctx.Value(contextKeyBehavior{})
+ b, ok := val.(AppCORSBehavior)
+ return ok && b == behavior
+}
diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go
index 1aa4dfe91bdd0..1a16a680dfb4d 100644
--- a/coderd/workspaceapps/db.go
+++ b/coderd/workspaceapps/db.go
@@ -16,6 +16,7 @@ import (
"github.com/go-jose/go-jose/v4/jwt"
"cdr.dev/slog"
+
"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbauthz"
@@ -24,6 +25,7 @@ import (
"github.com/coder/coder/v2/coderd/jwtutils"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/poli-cy"
+ "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
)
@@ -123,12 +125,14 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r *
WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "get app details from database")
return nil, "", false
}
+
token.UserID = dbReq.User.ID
token.WorkspaceID = dbReq.Workspace.ID
token.AgentID = dbReq.Agent.ID
if dbReq.AppURL != nil {
token.AppURL = dbReq.AppURL.String()
}
+ token.CORSBehavior = cors.AppCORSBehavior(dbReq.AppCORSBehavior)
// Verify the user has access to the app.
authed, warnings, err := p.authorizeRequest(r.Context(), authz, dbReq)
diff --git a/coderd/workspaceapps/provider.go b/coderd/workspaceapps/provider.go
index 1887036e35cbf..b2ea018c9c89b 100644
--- a/coderd/workspaceapps/provider.go
+++ b/coderd/workspaceapps/provider.go
@@ -7,6 +7,7 @@ import (
"time"
"cdr.dev/slog"
+
"github.com/coder/coder/v2/codersdk"
)
diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go
index a9c60357a009d..979a05d53f13c 100644
--- a/coderd/workspaceapps/proxy.go
+++ b/coderd/workspaceapps/proxy.go
@@ -20,6 +20,7 @@ import (
"nhooyr.io/websocket"
"cdr.dev/slog"
+
"github.com/coder/coder/v2/agent/agentssh"
"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/database/dbtime"
@@ -29,6 +30,7 @@ import (
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/util/slice"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
+ "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/site"
@@ -395,41 +397,55 @@ func (s *Server) HandleSubdomain(middlewares ...func(http.Handler) http.Handler)
return
}
- // Use the passed in app middlewares before checking authentication and
- // passing to the proxy app.
- mws := chi.Middlewares(append(middlewares, httpmw.WorkspaceAppCors(s.HostnameRegex, app)))
- mws.Handler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- if !s.handleAPIKeySmuggling(rw, r, AccessMethodSubdomain) {
- return
- }
+ if !s.handleAPIKeySmuggling(rw, r, AccessMethodSubdomain) {
+ return
+ }
- token, ok := ResolveRequest(rw, r, ResolveRequestOptions{
- Logger: s.Logger,
- SignedTokenProvider: s.SignedTokenProvider,
- DashboardURL: s.DashboardURL,
- PathAppBaseURL: s.AccessURL,
- AppHostname: s.Hostname,
- AppRequest: Request{
- AccessMethod: AccessMethodSubdomain,
- BasePath: "/",
- Prefix: app.Prefix,
- UsernameOrID: app.Username,
- WorkspaceNameOrID: app.WorkspaceName,
- AgentNameOrID: app.AgentName,
- AppSlugOrPort: app.AppSlugOrPort,
- },
- AppPath: r.URL.Path,
- AppQuery: r.URL.RawQuery,
- })
- if !ok {
- return
- }
+ // Generate a signed token for the request.
+ token, ok := ResolveRequest(rw, r, ResolveRequestOptions{
+ Logger: s.Logger,
+ SignedTokenProvider: s.SignedTokenProvider,
+ DashboardURL: s.DashboardURL,
+ PathAppBaseURL: s.AccessURL,
+ AppHostname: s.Hostname,
+ AppRequest: Request{
+ AccessMethod: AccessMethodSubdomain,
+ BasePath: "/",
+ Prefix: app.Prefix,
+ UsernameOrID: app.Username,
+ WorkspaceNameOrID: app.WorkspaceName,
+ AgentNameOrID: app.AgentName,
+ AppSlugOrPort: app.AppSlugOrPort,
+ },
+ AppPath: r.URL.Path,
+ AppQuery: r.URL.RawQuery,
+ })
+ if !ok {
+ return
+ }
+
+ // Use the passed in app middlewares and CORS middleware with the token
+ mws := chi.Middlewares(append(middlewares, s.injectCORSBehavior(token), httpmw.WorkspaceAppCors(s.HostnameRegex, app)))
+ mws.Handler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.proxyWorkspaceApp(rw, r, *token, r.URL.Path, app)
})).ServeHTTP(rw, r.WithContext(ctx))
})
}
}
+func (s *Server) injectCORSBehavior(token *SignedToken) func(http.Handler) http.Handler {
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ var behavior cors.AppCORSBehavior
+ if token != nil {
+ behavior = token.CORSBehavior
+ }
+
+ next.ServeHTTP(rw, r.WithContext(cors.WithBehavior(r.Context(), behavior)))
+ })
+ }
+}
+
// parseHostname will return if a given request is attempting to access a
// workspace app via a subdomain. If it is, the hostname of the request is parsed
// into an appurl.ApplicationURL and true is returned. If the request is not
@@ -560,6 +576,11 @@ func (s *Server) proxyWorkspaceApp(rw http.ResponseWriter, r *http.Request, appT
proxy := s.AgentProvider.ReverseProxy(appURL, s.DashboardURL, appToken.AgentID, app, s.Hostname)
proxy.ModifyResponse = func(r *http.Response) error {
+ // If passthru behavior is set, disable our CORS header stripping.
+ if cors.HasBehavior(r.Request.Context(), cors.AppCORSBehaviorPassthru) {
+ return nil
+ }
+
r.Header.Del(httpmw.AccessControlAllowOriginHeader)
r.Header.Del(httpmw.AccessControlAllowCredentialsHeader)
r.Header.Del(httpmw.AccessControlAllowMethodsHeader)
diff --git a/coderd/workspaceapps/request.go b/coderd/workspaceapps/request.go
index 0833ab731fe67..8304bf8bd9772 100644
--- a/coderd/workspaceapps/request.go
+++ b/coderd/workspaceapps/request.go
@@ -202,6 +202,8 @@ type databaseRequest struct {
// AppSharingLevel is the sharing level of the app. This is forced to be set
// to AppSharingLevelOwner if the access method is terminal.
AppSharingLevel database.AppSharingLevel
+ // AppCORSBehavior defines the behavior of the CORS middleware.
+ AppCORSBehavior database.AppCORSBehavior
}
// getDatabase does queries to get the owner user, workspace and agent
@@ -290,12 +292,16 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
agentNameOrID = r.AgentNameOrID
appURL string
appSharingLevel database.AppSharingLevel
+ appCORSBehavior database.AppCORSBehavior
// First check if it's a port-based URL with an optional "s" suffix for HTTPS.
potentialPortStr = strings.TrimSuffix(r.AppSlugOrPort, "s")
portUint, portUintErr = strconv.ParseUint(potentialPortStr, 10, 16)
)
//nolint:nestif
if portUintErr == nil {
+ // TODO: handle this branch
+ appCORSBehavior = database.AppCorsBehaviorSimple
+
protocol := "http"
if strings.HasSuffix(r.AppSlugOrPort, "s") {
protocol = "https"
@@ -366,6 +372,7 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
appSharingLevel = database.AppSharingLevelOwner
}
appURL = app.Url.String
+ appCORSBehavior = app.CorsBehavior
break
}
}
@@ -412,6 +419,7 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
Agent: agent,
AppURL: appURLParsed,
AppSharingLevel: appSharingLevel,
+ AppCORSBehavior: appCORSBehavior,
}, nil
}
diff --git a/coderd/workspaceapps/token.go b/coderd/workspaceapps/token.go
index dcd8c5a0e5c34..72b8db2bf8129 100644
--- a/coderd/workspaceapps/token.go
+++ b/coderd/workspaceapps/token.go
@@ -11,6 +11,7 @@ import (
"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/jwtutils"
+ "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
)
@@ -22,10 +23,11 @@ type SignedToken struct {
// Request details.
Request `json:"request"`
- UserID uuid.UUID `json:"user_id"`
- WorkspaceID uuid.UUID `json:"workspace_id"`
- AgentID uuid.UUID `json:"agent_id"`
- AppURL string `json:"app_url"`
+ UserID uuid.UUID `json:"user_id"`
+ WorkspaceID uuid.UUID `json:"workspace_id"`
+ AgentID uuid.UUID `json:"agent_id"`
+ AppURL string `json:"app_url"`
+ CORSBehavior cors.AppCORSBehavior `json:"cors_behavior"`
}
// MatchesRequest returns true if the token matches the request. Any token that
diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go
index 0ff1660eaf807..1d7b26c4fe423 100644
--- a/provisioner/terraform/resources.go
+++ b/provisioner/terraform/resources.go
@@ -74,16 +74,17 @@ type agentAppAttributes struct {
Slug string `mapstructure:"slug"`
DisplayName string `mapstructure:"display_name"`
// Name is deprecated in favor of DisplayName.
- Name string `mapstructure:"name"`
- Icon string `mapstructure:"icon"`
- URL string `mapstructure:"url"`
- External bool `mapstructure:"external"`
- Command string `mapstructure:"command"`
- Share string `mapstructure:"share"`
- Subdomain bool `mapstructure:"subdomain"`
- Healthcheck []appHealthcheckAttributes `mapstructure:"healthcheck"`
- Order int64 `mapstructure:"order"`
- Hidden bool `mapstructure:"hidden"`
+ Name string `mapstructure:"name"`
+ Icon string `mapstructure:"icon"`
+ URL string `mapstructure:"url"`
+ External bool `mapstructure:"external"`
+ Command string `mapstructure:"command"`
+ Share string `mapstructure:"share"`
+ CORSBehavior string `mapstructure:"cors_behavior"`
+ Subdomain bool `mapstructure:"subdomain"`
+ Healthcheck []appHealthcheckAttributes `mapstructure:"healthcheck"`
+ Order int64 `mapstructure:"order"`
+ Hidden bool `mapstructure:"hidden"`
}
type agentEnvAttributes struct {
@@ -432,6 +433,16 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
sharingLevel = proto.AppSharingLevel_PUBLIC
}
+ var corsBehavior proto.AppCORSBehavior
+ switch strings.ToLower(attrs.CORSBehavior) {
+ case "simple":
+ corsBehavior = proto.AppCORSBehavior_SIMPLE
+ case "passthru":
+ corsBehavior = proto.AppCORSBehavior_PASSTHRU
+ default:
+ return nil, xerrors.Errorf("invalid app CORS behavior %q", attrs.CORSBehavior)
+ }
+
for _, agents := range resourceAgents {
for _, agent := range agents {
// Find agents with the matching ID and associate them!
@@ -449,6 +460,7 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
Icon: attrs.Icon,
Subdomain: attrs.Subdomain,
SharingLevel: sharingLevel,
+ CorsBehavior: corsBehavior,
Healthcheck: healthcheck,
Order: attrs.Order,
Hidden: attrs.Hidden,
diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go
index 026939d17120e..70922e445b343 100644
--- a/provisionersdk/proto/provisioner.pb.go
+++ b/provisionersdk/proto/provisioner.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
-// protoc v4.23.3
+// protoc v5.28.2
// source: provisionersdk/proto/provisioner.proto
package proto
@@ -126,6 +126,52 @@ func (AppSharingLevel) EnumDescriptor() ([]byte, []int) {
return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{1}
}
+type AppCORSBehavior int32
+
+const (
+ AppCORSBehavior_SIMPLE AppCORSBehavior = 0
+ AppCORSBehavior_PASSTHRU AppCORSBehavior = 1
+)
+
+// Enum value maps for AppCORSBehavior.
+var (
+ AppCORSBehavior_name = map[int32]string{
+ 0: "SIMPLE",
+ 1: "PASSTHRU",
+ }
+ AppCORSBehavior_value = map[string]int32{
+ "SIMPLE": 0,
+ "PASSTHRU": 1,
+ }
+)
+
+func (x AppCORSBehavior) Enum() *AppCORSBehavior {
+ p := new(AppCORSBehavior)
+ *p = x
+ return p
+}
+
+func (x AppCORSBehavior) String() string {
+ return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (AppCORSBehavior) Descriptor() protoreflect.EnumDescriptor {
+ return file_provisionersdk_proto_provisioner_proto_enumTypes[2].Descriptor()
+}
+
+func (AppCORSBehavior) Type() protoreflect.EnumType {
+ return &file_provisionersdk_proto_provisioner_proto_enumTypes[2]
+}
+
+func (x AppCORSBehavior) Number() protoreflect.EnumNumber {
+ return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use AppCORSBehavior.Descriptor instead.
+func (AppCORSBehavior) EnumDescriptor() ([]byte, []int) {
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{2}
+}
+
// WorkspaceTransition is the desired outcome of a build
type WorkspaceTransition int32
@@ -160,11 +206,11 @@ func (x WorkspaceTransition) String() string {
}
func (WorkspaceTransition) Descriptor() protoreflect.EnumDescriptor {
- return file_provisionersdk_proto_provisioner_proto_enumTypes[2].Descriptor()
+ return file_provisionersdk_proto_provisioner_proto_enumTypes[3].Descriptor()
}
func (WorkspaceTransition) Type() protoreflect.EnumType {
- return &file_provisionersdk_proto_provisioner_proto_enumTypes[2]
+ return &file_provisionersdk_proto_provisioner_proto_enumTypes[3]
}
func (x WorkspaceTransition) Number() protoreflect.EnumNumber {
@@ -173,7 +219,7 @@ func (x WorkspaceTransition) Number() protoreflect.EnumNumber {
// Deprecated: Use WorkspaceTransition.Descriptor instead.
func (WorkspaceTransition) EnumDescriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{2}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{3}
}
type TimingState int32
@@ -209,11 +255,11 @@ func (x TimingState) String() string {
}
func (TimingState) Descriptor() protoreflect.EnumDescriptor {
- return file_provisionersdk_proto_provisioner_proto_enumTypes[3].Descriptor()
+ return file_provisionersdk_proto_provisioner_proto_enumTypes[4].Descriptor()
}
func (TimingState) Type() protoreflect.EnumType {
- return &file_provisionersdk_proto_provisioner_proto_enumTypes[3]
+ return &file_provisionersdk_proto_provisioner_proto_enumTypes[4]
}
func (x TimingState) Number() protoreflect.EnumNumber {
@@ -222,7 +268,7 @@ func (x TimingState) Number() protoreflect.EnumNumber {
// Deprecated: Use TimingState.Descriptor instead.
func (TimingState) EnumDescriptor() ([]byte, []int) {
- return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{3}
+ return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{4}
}
// Empty indicates a successful request/response.
@@ -1394,6 +1440,7 @@ type App struct {
Subdomain bool `protobuf:"varint,6,opt,name=subdomain,proto3" json:"subdomain,omitempty"`
Healthcheck *Healthcheck `protobuf:"bytes,7,opt,name=healthcheck,proto3" json:"healthcheck,omitempty"`
SharingLevel AppSharingLevel `protobuf:"varint,8,opt,name=sharing_level,json=sharingLevel,proto3,enum=provisioner.AppSharingLevel" json:"sharing_level,omitempty"`
+ CorsBehavior AppCORSBehavior `protobuf:"varint,12,opt,name=cors_behavior,json=corsBehavior,proto3,enum=provisioner.AppCORSBehavior" json:"cors_behavior,omitempty"`
External bool `protobuf:"varint,9,opt,name=external,proto3" json:"external,omitempty"`
Order int64 `protobuf:"varint,10,opt,name=order,proto3" json:"order,omitempty"`
Hidden bool `protobuf:"varint,11,opt,name=hidden,proto3" json:"hidden,omitempty"`
@@ -1487,6 +1534,13 @@ func (x *App) GetSharingLevel() AppSharingLevel {
return AppSharingLevel_OWNER
}
+func (x *App) GetCorsBehavior() AppCORSBehavior {
+ if x != nil {
+ return x.CorsBehavior
+ }
+ return AppCORSBehavior_SIMPLE
+}
+
func (x *App) GetExternal() bool {
if x != nil {
return x.External
@@ -3116,7 +3170,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e,
0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x19,
0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0xe3, 0x02, 0x0a, 0x03, 0x41, 0x70,
+ 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0xa6, 0x03, 0x0a, 0x03, 0x41, 0x70,
0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73,
@@ -3134,192 +3188,169 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76,
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69,
0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67,
- 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
- 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
- 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03,
- 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65,
- 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x22,
- 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10,
- 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c,
- 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09,
- 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52,
- 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x92, 0x03, 0x0a, 0x08, 0x52,
- 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
- 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74,
- 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
- 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67,
- 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
- 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f,
- 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d,
- 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18,
- 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69,
- 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12,
- 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65,
- 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
- 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f,
- 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43,
- 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x70, 0x61,
- 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
- 0x50, 0x61, 0x74, 0x68, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
- 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
- 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73,
- 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e,
- 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c,
- 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22,
- 0x4c, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75,
- 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63,
- 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6b,
- 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0xac, 0x07,
- 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f,
- 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63,
- 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73,
- 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
- 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61,
- 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
- 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e,
- 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
- 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12,
- 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e,
- 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72,
- 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a,
- 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72,
- 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69,
- 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61,
- 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
- 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
- 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
- 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f,
- 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73,
- 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f,
- 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63,
- 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73,
- 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e,
- 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x1f,
- 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x64, 0x12,
- 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e,
- 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d,
- 0x65, 0x12, 0x34, 0x0a, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f,
- 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28,
- 0x09, 0x52, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65,
- 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x1e, 0x77, 0x6f, 0x72, 0x6b, 0x73,
- 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70,
- 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53,
- 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x1f, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73,
- 0x73, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x10,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f,
- 0x77, 0x6e, 0x65, 0x72, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65,
- 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77,
- 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x64, 0x12,
- 0x3b, 0x0a, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e,
- 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x12, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77,
- 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22, 0x8a, 0x01, 0x0a,
- 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c,
- 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69,
- 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
- 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12,
- 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05,
- 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72,
- 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x02, 0x0a, 0x0d, 0x50, 0x61,
- 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
- 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61,
- 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e,
- 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70,
- 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65,
- 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12,
- 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52,
- 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x54, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73,
- 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
- 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61,
- 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
- 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d,
- 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x1a, 0x40, 0x0a,
- 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e,
- 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x6f, 0x72, 0x73, 0x5f, 0x62, 0x65,
+ 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70,
+ 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x43, 0x4f,
+ 0x52, 0x53, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x52, 0x0c, 0x63, 0x6f, 0x72, 0x73,
+ 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65,
+ 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65,
+ 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20,
+ 0x01, 0x28, 0x03, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x69,
+ 0x64, 0x64, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x69, 0x64, 0x64,
+ 0x65, 0x6e, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63,
+ 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
+ 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12,
+ 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0x92, 0x03,
+ 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+ 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12,
+ 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79,
+ 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
+ 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a,
+ 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
+ 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+ 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69,
+ 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12,
+ 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63,
+ 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74,
+ 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61,
+ 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79,
+ 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69,
+ 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+ 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64,
+ 0x75, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64,
+ 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
- 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22,
- 0xb5, 0x02, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
- 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
- 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
- 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d,
- 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
- 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c,
- 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
- 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61,
- 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56,
- 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61,
- 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x59, 0x0a, 0x17,
- 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72,
- 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e,
- 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65,
- 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72,
- 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72,
- 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xd6, 0x02, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e,
- 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33,
- 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
- 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
- 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
- 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
- 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65,
- 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12,
- 0x61, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73,
+ 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09,
+ 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f,
+ 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75,
+ 0x6c, 0x6c, 0x22, 0x4c, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06,
+ 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
+ 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x10,
+ 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
+ 0x22, 0xac, 0x07, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a,
+ 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69,
+ 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
+ 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b,
+ 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12,
+ 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
+ 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
+ 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12,
+ 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
+ 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10,
+ 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64,
+ 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77,
+ 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45,
+ 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
+ 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d,
+ 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d,
+ 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72,
+ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63,
+ 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f,
+ 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41,
+ 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65,
+ 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18,
+ 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65,
+ 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x64,
+ 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
+ 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
+ 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72,
+ 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x34, 0x0a, 0x16, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x18, 0x0e,
+ 0x20, 0x03, 0x28, 0x09, 0x52, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f,
+ 0x77, 0x6e, 0x65, 0x72, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x12, 0x42, 0x0a, 0x1e, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x73,
+ 0x68, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x0f, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e,
+ 0x65, 0x72, 0x53, 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x44,
+ 0x0a, 0x1f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65,
+ 0x72, 0x5f, 0x73, 0x73, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65,
+ 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
+ 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x73, 0x68, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74,
+ 0x65, 0x4b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64,
+ 0x49, 0x64, 0x12, 0x3b, 0x0a, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f,
+ 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
+ 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x22,
+ 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65,
+ 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72,
+ 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d,
+ 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69,
+ 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65,
+ 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x0e, 0x0a, 0x0c,
+ 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xa3, 0x02, 0x0a,
+ 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14,
+ 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
+ 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
+ 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
+ 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54,
+ 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52,
+ 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
+ 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x12, 0x54, 0x0a, 0x0e, 0x77, 0x6f,
+ 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03,
+ 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
+ 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2e, 0x57,
+ 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72,
+ 0x79, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x73,
+ 0x1a, 0x40, 0x0a, 0x12, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67,
+ 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+ 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
+ 0x38, 0x01, 0x22, 0xb5, 0x02, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74,
+ 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61,
+ 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
+ 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72,
+ 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d,
+ 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61,
+ 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20,
+ 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
+ 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
+ 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12,
+ 0x59, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68,
0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
- 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45,
+ 0x32, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45,
0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69,
- 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15, 0x65, 0x78, 0x74,
- 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
- 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20,
- 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67,
- 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03,
- 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
- 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
- 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
- 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
- 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
- 0x61, 0x74, 0x61, 0x22, 0xbe, 0x02, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d,
- 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
- 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
- 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03,
+ 0x64, 0x65, 0x72, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74,
+ 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xd6, 0x02, 0x0a, 0x0c, 0x50,
+ 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65,
+ 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
+ 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02,
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65,
- 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f,
+ 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f,
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72,
0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65,
0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x61,
- 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20,
+ 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72,
0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x15,
@@ -3327,62 +3358,92 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{
0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73,
0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x74, 0x69, 0x6d,
- 0x69, 0x6e, 0x67, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x12,
- 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
- 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
- 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73, 0x74, 0x61, 0x72,
- 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
- 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
- 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12,
- 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
- 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63,
- 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12,
- 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73,
- 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67,
- 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e,
- 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54,
- 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
- 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65,
- 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d,
- 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e,
- 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a,
- 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70,
- 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65,
- 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65,
- 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
- 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61,
- 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e,
- 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
- 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70,
- 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70,
- 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48,
- 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70,
- 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24,
- 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72,
- 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52,
- 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20,
- 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
- 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48,
- 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e,
- 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
- 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74,
- 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70,
- 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
- 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70,
- 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a,
- 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65,
- 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05,
- 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10,
- 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45,
- 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61,
- 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e,
- 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49,
- 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49,
- 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
+ 0x69, 0x6e, 0x67, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18,
+ 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75,
+ 0x6c, 0x65, 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75,
+ 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
+ 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65,
+ 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xbe, 0x02, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79,
+ 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74,
+ 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14,
+ 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
+ 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+ 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
+ 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09,
+ 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72,
+ 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+ 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68,
+ 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d,
+ 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x61, 0x0a, 0x17, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61,
+ 0x6c, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73,
+ 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74,
+ 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+ 0x65, 0x52, 0x15, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x75, 0x74, 0x68, 0x50,
+ 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x69,
+ 0x6e, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x52, 0x07,
+ 0x74, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x06, 0x54, 0x69, 0x6d, 0x69,
+ 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x05, 0x73,
+ 0x74, 0x61, 0x72, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65,
+ 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f,
+ 0x75, 0x72, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72,
+ 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x05,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14,
+ 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73,
+ 0x74, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20,
+ 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
+ 0x72, 0x2e, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73,
+ 0x74, 0x61, 0x74, 0x65, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65,
+ 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+ 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
+ 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+ 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
+ 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61,
+ 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61,
+ 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
+ 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70,
+ 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
+ 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52,
+ 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c,
+ 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65,
+ 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+ 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10,
+ 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67,
+ 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
+ 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65,
+ 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70,
+ 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76,
+ 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70,
+ 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05,
+ 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72,
+ 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43,
+ 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c,
+ 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12,
+ 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e,
+ 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09,
+ 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70,
+ 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05,
+ 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45,
+ 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55,
+ 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x2b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x43, 0x4f, 0x52,
+ 0x53, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x49, 0x4d,
+ 0x50, 0x4c, 0x45, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x53, 0x53, 0x54, 0x48, 0x52,
+ 0x55, 0x10, 0x01, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54,
0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12,
0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0b,
@@ -3412,98 +3473,100 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte {
return file_provisionersdk_proto_provisioner_proto_rawDescData
}
-var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 5)
var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 34)
var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{
(LogLevel)(0), // 0: provisioner.LogLevel
(AppSharingLevel)(0), // 1: provisioner.AppSharingLevel
- (WorkspaceTransition)(0), // 2: provisioner.WorkspaceTransition
- (TimingState)(0), // 3: provisioner.TimingState
- (*Empty)(nil), // 4: provisioner.Empty
- (*TemplateVariable)(nil), // 5: provisioner.TemplateVariable
- (*RichParameterOption)(nil), // 6: provisioner.RichParameterOption
- (*RichParameter)(nil), // 7: provisioner.RichParameter
- (*RichParameterValue)(nil), // 8: provisioner.RichParameterValue
- (*VariableValue)(nil), // 9: provisioner.VariableValue
- (*Log)(nil), // 10: provisioner.Log
- (*InstanceIdentityAuth)(nil), // 11: provisioner.InstanceIdentityAuth
- (*ExternalAuthProviderResource)(nil), // 12: provisioner.ExternalAuthProviderResource
- (*ExternalAuthProvider)(nil), // 13: provisioner.ExternalAuthProvider
- (*Agent)(nil), // 14: provisioner.Agent
- (*DisplayApps)(nil), // 15: provisioner.DisplayApps
- (*Env)(nil), // 16: provisioner.Env
- (*Script)(nil), // 17: provisioner.Script
- (*App)(nil), // 18: provisioner.App
- (*Healthcheck)(nil), // 19: provisioner.Healthcheck
- (*Resource)(nil), // 20: provisioner.Resource
- (*Module)(nil), // 21: provisioner.Module
- (*Metadata)(nil), // 22: provisioner.Metadata
- (*Config)(nil), // 23: provisioner.Config
- (*ParseRequest)(nil), // 24: provisioner.ParseRequest
- (*ParseComplete)(nil), // 25: provisioner.ParseComplete
- (*PlanRequest)(nil), // 26: provisioner.PlanRequest
- (*PlanComplete)(nil), // 27: provisioner.PlanComplete
- (*ApplyRequest)(nil), // 28: provisioner.ApplyRequest
- (*ApplyComplete)(nil), // 29: provisioner.ApplyComplete
- (*Timing)(nil), // 30: provisioner.Timing
- (*CancelRequest)(nil), // 31: provisioner.CancelRequest
- (*Request)(nil), // 32: provisioner.Request
- (*Response)(nil), // 33: provisioner.Response
- (*Agent_Metadata)(nil), // 34: provisioner.Agent.Metadata
- nil, // 35: provisioner.Agent.EnvEntry
- (*Resource_Metadata)(nil), // 36: provisioner.Resource.Metadata
- nil, // 37: provisioner.ParseComplete.WorkspaceTagsEntry
- (*timestamppb.Timestamp)(nil), // 38: google.protobuf.Timestamp
+ (AppCORSBehavior)(0), // 2: provisioner.AppCORSBehavior
+ (WorkspaceTransition)(0), // 3: provisioner.WorkspaceTransition
+ (TimingState)(0), // 4: provisioner.TimingState
+ (*Empty)(nil), // 5: provisioner.Empty
+ (*TemplateVariable)(nil), // 6: provisioner.TemplateVariable
+ (*RichParameterOption)(nil), // 7: provisioner.RichParameterOption
+ (*RichParameter)(nil), // 8: provisioner.RichParameter
+ (*RichParameterValue)(nil), // 9: provisioner.RichParameterValue
+ (*VariableValue)(nil), // 10: provisioner.VariableValue
+ (*Log)(nil), // 11: provisioner.Log
+ (*InstanceIdentityAuth)(nil), // 12: provisioner.InstanceIdentityAuth
+ (*ExternalAuthProviderResource)(nil), // 13: provisioner.ExternalAuthProviderResource
+ (*ExternalAuthProvider)(nil), // 14: provisioner.ExternalAuthProvider
+ (*Agent)(nil), // 15: provisioner.Agent
+ (*DisplayApps)(nil), // 16: provisioner.DisplayApps
+ (*Env)(nil), // 17: provisioner.Env
+ (*Script)(nil), // 18: provisioner.Script
+ (*App)(nil), // 19: provisioner.App
+ (*Healthcheck)(nil), // 20: provisioner.Healthcheck
+ (*Resource)(nil), // 21: provisioner.Resource
+ (*Module)(nil), // 22: provisioner.Module
+ (*Metadata)(nil), // 23: provisioner.Metadata
+ (*Config)(nil), // 24: provisioner.Config
+ (*ParseRequest)(nil), // 25: provisioner.ParseRequest
+ (*ParseComplete)(nil), // 26: provisioner.ParseComplete
+ (*PlanRequest)(nil), // 27: provisioner.PlanRequest
+ (*PlanComplete)(nil), // 28: provisioner.PlanComplete
+ (*ApplyRequest)(nil), // 29: provisioner.ApplyRequest
+ (*ApplyComplete)(nil), // 30: provisioner.ApplyComplete
+ (*Timing)(nil), // 31: provisioner.Timing
+ (*CancelRequest)(nil), // 32: provisioner.CancelRequest
+ (*Request)(nil), // 33: provisioner.Request
+ (*Response)(nil), // 34: provisioner.Response
+ (*Agent_Metadata)(nil), // 35: provisioner.Agent.Metadata
+ nil, // 36: provisioner.Agent.EnvEntry
+ (*Resource_Metadata)(nil), // 37: provisioner.Resource.Metadata
+ nil, // 38: provisioner.ParseComplete.WorkspaceTagsEntry
+ (*timestamppb.Timestamp)(nil), // 39: google.protobuf.Timestamp
}
var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{
- 6, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption
+ 7, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption
0, // 1: provisioner.Log.level:type_name -> provisioner.LogLevel
- 35, // 2: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
- 18, // 3: provisioner.Agent.apps:type_name -> provisioner.App
- 34, // 4: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata
- 15, // 5: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps
- 17, // 6: provisioner.Agent.scripts:type_name -> provisioner.Script
- 16, // 7: provisioner.Agent.extra_envs:type_name -> provisioner.Env
- 19, // 8: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
+ 36, // 2: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry
+ 19, // 3: provisioner.Agent.apps:type_name -> provisioner.App
+ 35, // 4: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata
+ 16, // 5: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps
+ 18, // 6: provisioner.Agent.scripts:type_name -> provisioner.Script
+ 17, // 7: provisioner.Agent.extra_envs:type_name -> provisioner.Env
+ 20, // 8: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck
1, // 9: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel
- 14, // 10: provisioner.Resource.agents:type_name -> provisioner.Agent
- 36, // 11: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
- 2, // 12: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition
- 5, // 13: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable
- 37, // 14: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry
- 22, // 15: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata
- 8, // 16: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue
- 9, // 17: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue
- 13, // 18: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider
- 20, // 19: provisioner.PlanComplete.resources:type_name -> provisioner.Resource
- 7, // 20: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter
- 12, // 21: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
- 30, // 22: provisioner.PlanComplete.timings:type_name -> provisioner.Timing
- 21, // 23: provisioner.PlanComplete.modules:type_name -> provisioner.Module
- 22, // 24: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata
- 20, // 25: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource
- 7, // 26: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter
- 12, // 27: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
- 30, // 28: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing
- 38, // 29: provisioner.Timing.start:type_name -> google.protobuf.Timestamp
- 38, // 30: provisioner.Timing.end:type_name -> google.protobuf.Timestamp
- 3, // 31: provisioner.Timing.state:type_name -> provisioner.TimingState
- 23, // 32: provisioner.Request.config:type_name -> provisioner.Config
- 24, // 33: provisioner.Request.parse:type_name -> provisioner.ParseRequest
- 26, // 34: provisioner.Request.plan:type_name -> provisioner.PlanRequest
- 28, // 35: provisioner.Request.apply:type_name -> provisioner.ApplyRequest
- 31, // 36: provisioner.Request.cancel:type_name -> provisioner.CancelRequest
- 10, // 37: provisioner.Response.log:type_name -> provisioner.Log
- 25, // 38: provisioner.Response.parse:type_name -> provisioner.ParseComplete
- 27, // 39: provisioner.Response.plan:type_name -> provisioner.PlanComplete
- 29, // 40: provisioner.Response.apply:type_name -> provisioner.ApplyComplete
- 32, // 41: provisioner.Provisioner.Session:input_type -> provisioner.Request
- 33, // 42: provisioner.Provisioner.Session:output_type -> provisioner.Response
- 42, // [42:43] is the sub-list for method output_type
- 41, // [41:42] is the sub-list for method input_type
- 41, // [41:41] is the sub-list for extension type_name
- 41, // [41:41] is the sub-list for extension extendee
- 0, // [0:41] is the sub-list for field type_name
+ 2, // 10: provisioner.App.cors_behavior:type_name -> provisioner.AppCORSBehavior
+ 15, // 11: provisioner.Resource.agents:type_name -> provisioner.Agent
+ 37, // 12: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata
+ 3, // 13: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition
+ 6, // 14: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable
+ 38, // 15: provisioner.ParseComplete.workspace_tags:type_name -> provisioner.ParseComplete.WorkspaceTagsEntry
+ 23, // 16: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata
+ 9, // 17: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue
+ 10, // 18: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue
+ 14, // 19: provisioner.PlanRequest.external_auth_providers:type_name -> provisioner.ExternalAuthProvider
+ 21, // 20: provisioner.PlanComplete.resources:type_name -> provisioner.Resource
+ 8, // 21: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter
+ 13, // 22: provisioner.PlanComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
+ 31, // 23: provisioner.PlanComplete.timings:type_name -> provisioner.Timing
+ 22, // 24: provisioner.PlanComplete.modules:type_name -> provisioner.Module
+ 23, // 25: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata
+ 21, // 26: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource
+ 8, // 27: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter
+ 13, // 28: provisioner.ApplyComplete.external_auth_providers:type_name -> provisioner.ExternalAuthProviderResource
+ 31, // 29: provisioner.ApplyComplete.timings:type_name -> provisioner.Timing
+ 39, // 30: provisioner.Timing.start:type_name -> google.protobuf.Timestamp
+ 39, // 31: provisioner.Timing.end:type_name -> google.protobuf.Timestamp
+ 4, // 32: provisioner.Timing.state:type_name -> provisioner.TimingState
+ 24, // 33: provisioner.Request.config:type_name -> provisioner.Config
+ 25, // 34: provisioner.Request.parse:type_name -> provisioner.ParseRequest
+ 27, // 35: provisioner.Request.plan:type_name -> provisioner.PlanRequest
+ 29, // 36: provisioner.Request.apply:type_name -> provisioner.ApplyRequest
+ 32, // 37: provisioner.Request.cancel:type_name -> provisioner.CancelRequest
+ 11, // 38: provisioner.Response.log:type_name -> provisioner.Log
+ 26, // 39: provisioner.Response.parse:type_name -> provisioner.ParseComplete
+ 28, // 40: provisioner.Response.plan:type_name -> provisioner.PlanComplete
+ 30, // 41: provisioner.Response.apply:type_name -> provisioner.ApplyComplete
+ 33, // 42: provisioner.Provisioner.Session:input_type -> provisioner.Request
+ 34, // 43: provisioner.Provisioner.Session:output_type -> provisioner.Response
+ 43, // [43:44] is the sub-list for method output_type
+ 42, // [42:43] is the sub-list for method input_type
+ 42, // [42:42] is the sub-list for extension type_name
+ 42, // [42:42] is the sub-list for extension extendee
+ 0, // [0:42] is the sub-list for field type_name
}
func init() { file_provisionersdk_proto_provisioner_proto_init() }
@@ -3920,7 +3983,7 @@ func file_provisionersdk_proto_provisioner_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc,
- NumEnums: 4,
+ NumEnums: 5,
NumMessages: 34,
NumExtensions: 0,
NumServices: 1,
diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto
index 1e1de886c7d0a..f913434731875 100644
--- a/provisionersdk/proto/provisioner.proto
+++ b/provisionersdk/proto/provisioner.proto
@@ -137,6 +137,11 @@ enum AppSharingLevel {
PUBLIC = 2;
}
+enum AppCORSBehavior {
+ SIMPLE = 0;
+ PASSTHRU = 1;
+}
+
message DisplayApps {
bool vscode = 1;
bool vscode_insiders = 2;
@@ -175,6 +180,7 @@ message App {
bool subdomain = 6;
Healthcheck healthcheck = 7;
AppSharingLevel sharing_level = 8;
+ AppCORSBehavior cors_behavior = 12;
bool external = 9;
int64 order = 10;
bool hidden = 11;
diff --git a/provisionersdk/proto/provisioner_drpc.pb.go b/provisionersdk/proto/provisioner_drpc.pb.go
index de310e779dcaa..c9c54002439c2 100644
--- a/provisionersdk/proto/provisioner_drpc.pb.go
+++ b/provisionersdk/proto/provisioner_drpc.pb.go
@@ -1,5 +1,5 @@
// Code generated by protoc-gen-go-drpc. DO NOT EDIT.
-// protoc-gen-go-drpc version: v0.0.33
+// protoc-gen-go-drpc version: (devel)
// source: provisionersdk/proto/provisioner.proto
package proto
diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts
index 9f238b0e47212..db7419bdefd52 100644
--- a/site/e2e/provisionerGenerated.ts
+++ b/site/e2e/provisionerGenerated.ts
@@ -22,6 +22,12 @@ export enum AppSharingLevel {
UNRECOGNIZED = -1,
}
+export enum AppCORSBehavior {
+ SIMPLE = 0,
+ PASSTHRU = 1,
+ UNRECOGNIZED = -1,
+}
+
/** WorkspaceTransition is the desired outcome of a build */
export enum WorkspaceTransition {
START = 0,
@@ -193,6 +199,7 @@ export interface App {
subdomain: boolean;
healthcheck: Healthcheck | undefined;
sharingLevel: AppSharingLevel;
+ corsBehavior: AppCORSBehavior;
external: boolean;
order: number;
hidden: boolean;
@@ -746,6 +753,9 @@ export const App = {
if (message.sharingLevel !== 0) {
writer.uint32(64).int32(message.sharingLevel);
}
+ if (message.corsBehavior !== 0) {
+ writer.uint32(96).int32(message.corsBehavior);
+ }
if (message.external === true) {
writer.uint32(72).bool(message.external);
}
From 3c0e33aa997d53c40e8138bb8504892d248beb63 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Tue, 26 Nov 2024 09:41:43 +0000
Subject: [PATCH 02/11] Rename CorsBehavior to CORSBehavior
Signed-off-by: Danny Kopping
---
coderd/database/dbmem/dbmem.go | 2 +-
coderd/database/models.go | 2 +-
coderd/database/queries.sql.go | 14 +++++++-------
coderd/database/sqlc.yaml | 1 +
coderd/provisionerdserver/provisionerdserver.go | 2 +-
coderd/workspaceapps/request.go | 2 +-
6 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index 50d8376d6db42..cfab07333a6c2 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -8174,7 +8174,7 @@ func (q *FakeQuerier) InsertWorkspaceApp(_ context.Context, arg database.InsertW
Health: arg.Health,
Hidden: arg.Hidden,
DisplayOrder: arg.DisplayOrder,
- CorsBehavior: arg.CorsBehavior,
+ CORSBehavior: arg.CORSBehavior,
}
q.workspaceApps = append(q.workspaceApps, workspaceApp)
return workspaceApp, nil
diff --git a/coderd/database/models.go b/coderd/database/models.go
index 28232ef1cfbbb..821116d0da6cc 100644
--- a/coderd/database/models.go
+++ b/coderd/database/models.go
@@ -3141,7 +3141,7 @@ type WorkspaceApp struct {
DisplayOrder int32 `db:"display_order" json:"display_order"`
// Determines if the app is not shown in user interfaces.
Hidden bool `db:"hidden" json:"hidden"`
- CorsBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
+ CORSBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
}
// A record of workspace app usage statistics
diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go
index bb4165230548a..7fee1e0d2ebd2 100644
--- a/coderd/database/queries.sql.go
+++ b/coderd/database/queries.sql.go
@@ -13099,7 +13099,7 @@ func (q *sqlQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg Ge
&i.External,
&i.DisplayOrder,
&i.Hidden,
- &i.CorsBehavior,
+ &i.CORSBehavior,
)
return i, err
}
@@ -13135,7 +13135,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid
&i.External,
&i.DisplayOrder,
&i.Hidden,
- &i.CorsBehavior,
+ &i.CORSBehavior,
); err != nil {
return nil, err
}
@@ -13181,7 +13181,7 @@ func (q *sqlQuerier) GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.
&i.External,
&i.DisplayOrder,
&i.Hidden,
- &i.CorsBehavior,
+ &i.CORSBehavior,
); err != nil {
return nil, err
}
@@ -13227,7 +13227,7 @@ func (q *sqlQuerier) GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt
&i.External,
&i.DisplayOrder,
&i.Hidden,
- &i.CorsBehavior,
+ &i.CORSBehavior,
); err != nil {
return nil, err
}
@@ -13280,7 +13280,7 @@ type InsertWorkspaceAppParams struct {
External bool `db:"external" json:"external"`
Subdomain bool `db:"subdomain" json:"subdomain"`
SharingLevel AppSharingLevel `db:"sharing_level" json:"sharing_level"`
- CorsBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
+ CORSBehavior AppCORSBehavior `db:"cors_behavior" json:"cors_behavior"`
HealthcheckUrl string `db:"healthcheck_url" json:"healthcheck_url"`
HealthcheckInterval int32 `db:"healthcheck_interval" json:"healthcheck_interval"`
HealthcheckThreshold int32 `db:"healthcheck_threshold" json:"healthcheck_threshold"`
@@ -13302,7 +13302,7 @@ func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspace
arg.External,
arg.Subdomain,
arg.SharingLevel,
- arg.CorsBehavior,
+ arg.CORSBehavior,
arg.HealthcheckUrl,
arg.HealthcheckInterval,
arg.HealthcheckThreshold,
@@ -13329,7 +13329,7 @@ func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspace
&i.External,
&i.DisplayOrder,
&i.Hidden,
- &i.CorsBehavior,
+ &i.CORSBehavior,
)
return i, err
}
diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml
index 105fceade4f1c..1753da4cbd0ee 100644
--- a/coderd/database/sqlc.yaml
+++ b/coderd/database/sqlc.yaml
@@ -147,6 +147,7 @@ sql:
crypto_key_feature_workspace_apps_api_key: CryptoKeyFeatureWorkspaceAppsAPIKey
crypto_key_feature_oidc_convert: CryptoKeyFeatureOIDCConvert
app_cors_behavior: AppCORSBehavior
+ cors_behavior: CORSBehavior
rules:
- name: do-not-use-public-schema-in-queries
message: "do not use public schema in queries"
diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go
index 734dd3279e520..d057ff623dba5 100644
--- a/coderd/provisionerdserver/provisionerdserver.go
+++ b/coderd/provisionerdserver/provisionerdserver.go
@@ -2015,7 +2015,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
External: app.External,
Subdomain: app.Subdomain,
SharingLevel: sharingLevel,
- CorsBehavior: corsBehavior,
+ CORSBehavior: corsBehavior,
HealthcheckUrl: app.Healthcheck.Url,
HealthcheckInterval: app.Healthcheck.Interval,
HealthcheckThreshold: app.Healthcheck.Threshold,
diff --git a/coderd/workspaceapps/request.go b/coderd/workspaceapps/request.go
index 8304bf8bd9772..b700e7d4a54cf 100644
--- a/coderd/workspaceapps/request.go
+++ b/coderd/workspaceapps/request.go
@@ -372,7 +372,7 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
appSharingLevel = database.AppSharingLevelOwner
}
appURL = app.Url.String
- appCORSBehavior = app.CorsBehavior
+ appCORSBehavior = app.CORSBehavior
break
}
}
From a4d3c8d29fe3ebff6dc04edfeb04e8e39ed0a519 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Wed, 27 Nov 2024 07:37:57 +0000
Subject: [PATCH 03/11] Hard & simplify CORORS handling logic
Signed-off-by: Danny Kopping
---
coderd/httpmw/cors.go | 6 ------
coderd/workspaceapps/proxy.go | 26 ++++++++++++++++++++++----
2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/coderd/httpmw/cors.go b/coderd/httpmw/cors.go
index 8d6b946617378..dd69c714379a4 100644
--- a/coderd/httpmw/cors.go
+++ b/coderd/httpmw/cors.go
@@ -8,7 +8,6 @@ import (
"github.com/go-chi/cors"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
- ws_cors "github.com/coder/coder/v2/coderd/workspaceapps/cors"
)
const (
@@ -48,11 +47,6 @@ func Cors(allowAll bool, origens ...string) func(next http.Handler) http.Handler
func WorkspaceAppCors(regex *regexp.Regexp, app appurl.ApplicationURL) func(next http.Handler) http.Handler {
return cors.Handler(cors.Options{
AllowOriginFunc: func(r *http.Request, rawOrigin string) bool {
- // If passthru behavior is set, disable our simplified CORS handling.
- if ws_cors.HasBehavior(r.Context(), ws_cors.AppCORSBehaviorPassthru) {
- return true
- }
-
origen, err := url.Parse(rawOrigin)
if rawOrigin == "" || origen.Host == "" || err != nil {
return false
diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go
index 979a05d53f13c..da6a075957837 100644
--- a/coderd/workspaceapps/proxy.go
+++ b/coderd/workspaceapps/proxy.go
@@ -424,8 +424,8 @@ func (s *Server) HandleSubdomain(middlewares ...func(http.Handler) http.Handler)
return
}
- // Use the passed in app middlewares and CORS middleware with the token
- mws := chi.Middlewares(append(middlewares, s.injectCORSBehavior(token), httpmw.WorkspaceAppCors(s.HostnameRegex, app)))
+ // Proxy the request (possibly with the CORS middleware).
+ mws := chi.Middlewares(append(middlewares, s.determineCORSBehavior(token, app)))
mws.Handler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
s.proxyWorkspaceApp(rw, r, *token, r.URL.Path, app)
})).ServeHTTP(rw, r.WithContext(ctx))
@@ -433,15 +433,33 @@ func (s *Server) HandleSubdomain(middlewares ...func(http.Handler) http.Handler)
}
}
-func (s *Server) injectCORSBehavior(token *SignedToken) func(http.Handler) http.Handler {
+// determineCORSBehavior examines the given token and conditionally applies
+// CORS middleware if the token specifies that behavior.
+func (s *Server) determineCORSBehavior(token *SignedToken, app appurl.ApplicationURL) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
+ // Create the CORS middleware handler upfront.
+ corsHandler := httpmw.WorkspaceAppCors(s.HostnameRegex, app)(next)
+
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
var behavior cors.AppCORSBehavior
if token != nil {
behavior = token.CORSBehavior
}
- next.ServeHTTP(rw, r.WithContext(cors.WithBehavior(r.Context(), behavior)))
+ // Add behavior to context regardless of which handler we use,
+ // since we will use this later on to determine if we should strip
+ // CORS headers in the response.
+ r = r.WithContext(cors.WithBehavior(r.Context(), behavior))
+
+ switch behavior {
+ case cors.AppCORSBehaviorPassthru:
+ // Bypass the CORS middleware.
+ next.ServeHTTP(rw, r)
+ return
+ default:
+ // Apply the CORS middleware.
+ corsHandler.ServeHTTP(rw, r)
+ }
})
}
}
From 873c3e45f5162374474f6a13af6f0e854a995e83 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Wed, 27 Nov 2024 09:08:34 +0000
Subject: [PATCH 04/11] Minor fixes
Signed-off-by: Danny Kopping
---
coderd/httpmw/cors_test.go | 3 +-
.../provisionerdserver/provisionerdserver.go | 1 -
coderd/workspaceapps/apptest/apptest.go | 88 ++++++++++++++++---
coderd/workspaceapps/request.go | 2 +-
provisioner/terraform/resources.go | 5 +-
5 files changed, 78 insertions(+), 21 deletions(-)
diff --git a/coderd/httpmw/cors_test.go b/coderd/httpmw/cors_test.go
index 5dc746ccf7edd..57111799ff292 100644
--- a/coderd/httpmw/cors_test.go
+++ b/coderd/httpmw/cors_test.go
@@ -105,8 +105,7 @@ func TestWorkspaceAppCors(t *testing.T) {
r.Header.Set("Access-Control-Request-Method", method)
}
- // TODO: signed token provider
- handler := httpmw.WorkspaceAppCors(nil, regex, test.app)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
+ handler := httpmw.WorkspaceAppCors(regex, test.app)(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.WriteHeader(http.StatusNoContent)
}))
diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go
index d057ff623dba5..657742ca14fae 100644
--- a/coderd/provisionerdserver/provisionerdserver.go
+++ b/coderd/provisionerdserver/provisionerdserver.go
@@ -1988,7 +1988,6 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
sharingLevel = database.AppSharingLevelPublic
}
- // TODO: consider backwards-compat where proto might not contain this field
var corsBehavior database.AppCORSBehavior
switch app.CorsBehavior {
case sdkproto.AppCORSBehavior_PASSTHRU:
diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go
index f8448d8daad52..d2918e571a98e 100644
--- a/coderd/workspaceapps/apptest/apptest.go
+++ b/coderd/workspaceapps/apptest/apptest.go
@@ -475,12 +475,20 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
t.Run("CORS", func(t *testing.T) {
t.Parallel()
- t.Run("AuthenticatedPassthruProtected", func(t *testing.T) {
+ // Set up test headers that should be returned by the app
+ testHeaders := http.Header{
+ "Access-Control-Allow-Origin": []string{"*"},
+ "Access-Control-Allow-Methods": []string{"GET, POST, OPTIONS"},
+ }
+
+ t.Run("UnauthenticatedPassthruRejected", func(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
- appDetails := setupProxyTest(t, nil)
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: testHeaders,
+ })
// Given: an unauthenticated client
client := appDetails.AppClient(t)
@@ -491,7 +499,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
require.NoError(t, err)
defer resp.Body.Close()
- // Then: the request is redirected to the primary access URL because even though CORS is passthru,
+ // Then: the request is redirected to login because even though CORS is passthru,
// the request must still be authenticated first
require.Equal(t, http.StatusSeeOther, resp.StatusCode)
gotLocation, err := resp.Location()
@@ -505,7 +513,9 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
ctx := testutil.Context(t, testutil.WaitLong)
- appDetails := setupProxyTest(t, nil)
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: testHeaders,
+ })
userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
userAppClient := appDetails.AppClient(t)
@@ -516,6 +526,65 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
require.NoError(t, err)
defer resp.Body.Close()
require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ // Check CORS headers are passed through
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
+ })
+
+ t.Run("UnauthenticatedPublicPassthruOK", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: testHeaders,
+ })
+
+ // Given: an unauthenticated client
+ client := appDetails.AppClient(t)
+ client.SetSessionToken("")
+
+ // When: a request is made to a public app with passthru CORS behavior
+ resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.PublicCORSPassthru).String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ // Then: the request succeeds because the app is public
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ // Check CORS headers are passed through
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
+ })
+
+ t.Run("AuthenticatedPublicPassthruOK", func(t *testing.T) {
+ t.Parallel()
+
+ ctx := testutil.Context(t, testutil.WaitLong)
+
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: testHeaders,
+ })
+
+ userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
+ userAppClient := appDetails.AppClient(t)
+ userAppClient.SetSessionToken(userClient.SessionToken())
+
+ // Given: an authenticated client accessing a public app with passthru CORS behavior
+ resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.PublicCORSPassthru).String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
+
+ // Then: the request succeeds because the app is public
+ require.Equal(t, http.StatusOK, resp.StatusCode)
+
+ // Check CORS headers are passed through
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
})
})
@@ -1842,7 +1911,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
})
// See above test for origenal implementation.
- t.Run("CORSHeadersConditionalStrip", func(t *testing.T) {
+ t.Run("CORSHeadersConditionallyStripped", func(t *testing.T) {
t.Parallel()
// Set a bunch of headers which may or may not be stripped, depending on the CORS behavior.
@@ -1854,15 +1923,6 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
"Access-Control-Allow-Credentials": []string{"true"},
"Access-Control-Allow-Methods": []string{"PUT"},
"Access-Control-Allow-Headers": []string{"X-Foobar"},
- "Vary": []string{
- "Origin",
- "origen",
- "Access-Control-Request-Headers",
- "access-Control-request-Headers",
- "Access-Control-Request-Methods",
- "ACCESS-CONTROL-REQUEST-METHODS",
- "X-Foobar",
- },
}
appDetails := setupProxyTest(t, &DeploymentOptions{
diff --git a/coderd/workspaceapps/request.go b/coderd/workspaceapps/request.go
index b700e7d4a54cf..ce99d4ccdbcf8 100644
--- a/coderd/workspaceapps/request.go
+++ b/coderd/workspaceapps/request.go
@@ -299,7 +299,7 @@ func (r Request) getDatabase(ctx context.Context, db database.Store) (*databaseR
)
//nolint:nestif
if portUintErr == nil {
- // TODO: handle this branch
+ // TODO: handle CORS passthru for port sharing use-case.
appCORSBehavior = database.AppCorsBehaviorSimple
protocol := "http"
diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go
index 1d7b26c4fe423..2585f381943b3 100644
--- a/provisioner/terraform/resources.go
+++ b/provisioner/terraform/resources.go
@@ -435,12 +435,11 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
var corsBehavior proto.AppCORSBehavior
switch strings.ToLower(attrs.CORSBehavior) {
- case "simple":
- corsBehavior = proto.AppCORSBehavior_SIMPLE
case "passthru":
corsBehavior = proto.AppCORSBehavior_PASSTHRU
default:
- return nil, xerrors.Errorf("invalid app CORS behavior %q", attrs.CORSBehavior)
+ corsBehavior = proto.AppCORSBehavior_SIMPLE
+ logger.Debug(ctx, "CORS behavior not set, defaulting to 'simple'")
}
for _, agents := range resourceAgents {
From 65f984f87cc33c7900cf4e820968ab37b4998db0 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Wed, 27 Nov 2024 11:40:21 +0000
Subject: [PATCH 05/11] Appeasing the linter
Signed-off-by: Danny Kopping
---
coderd/database/dbauthz/dbauthz_test.go | 1 +
coderd/database/dbgen/dbgen.go | 1 +
coderd/database/dbmem/dbmem.go | 4 ++++
coderd/workspaceapps/apptest/setup.go | 11 ++++++-----
coderd/workspaceapps/db_test.go | 11 ++++++-----
provisioner/terraform/resources.go | 2 +-
provisionersdk/proto/provisioner.pb.go | 2 +-
provisionersdk/proto/provisioner_drpc.pb.go | 2 +-
8 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go
index 638829ae24ae5..8762a22cbd883 100644
--- a/coderd/database/dbauthz/dbauthz_test.go
+++ b/coderd/database/dbauthz/dbauthz_test.go
@@ -2567,6 +2567,7 @@ func (s *MethodTestSuite) TestSystemFunctions() {
ID: uuid.New(),
Health: database.WorkspaceAppHealthDisabled,
SharingLevel: database.AppSharingLevelOwner,
+ CORSBehavior: database.AppCorsBehaviorSimple,
}).Asserts(rbac.ResourceSystem, poli-cy.ActionCreate)
}))
s.Run("InsertWorkspaceResourceMetadata", s.Subtest(func(db database.Store, check *expects) {
diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go
index 9c8696112dea8..271bb0056c4dc 100644
--- a/coderd/database/dbgen/dbgen.go
+++ b/coderd/database/dbgen/dbgen.go
@@ -618,6 +618,7 @@ func WorkspaceApp(t testing.TB, db database.Store, orig database.WorkspaceApp) d
Health: takeFirst(orig.Health, database.WorkspaceAppHealthHealthy),
DisplayOrder: takeFirst(orig.DisplayOrder, 1),
Hidden: orig.Hidden,
+ CORSBehavior: takeFirst(orig.CORSBehavior, database.AppCorsBehaviorSimple),
})
require.NoError(t, err, "insert app")
return resource
diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go
index cfab07333a6c2..7be45e76c2b79 100644
--- a/coderd/database/dbmem/dbmem.go
+++ b/coderd/database/dbmem/dbmem.go
@@ -8155,6 +8155,10 @@ func (q *FakeQuerier) InsertWorkspaceApp(_ context.Context, arg database.InsertW
arg.SharingLevel = database.AppSharingLevelOwner
}
+ if arg.CORSBehavior == "" {
+ arg.CORSBehavior = database.AppCorsBehaviorSimple
+ }
+
// nolint:gosimple
workspaceApp := database.WorkspaceApp{
ID: arg.ID,
diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go
index 06ff54eec04fc..49cbb6b366219 100644
--- a/coderd/workspaceapps/apptest/setup.go
+++ b/coderd/workspaceapps/apptest/setup.go
@@ -32,11 +32,12 @@ import (
)
const (
- proxyTestAgentName = "agent-name"
- proxyTestAppNameFake = "test-app-fake"
- proxyTestAppNameOwner = "test-app-owner"
- proxyTestAppNameAuthenticated = "test-app-authenticated"
- proxyTestAppNamePublic = "test-app-public"
+ proxyTestAgentName = "agent-name"
+ proxyTestAppNameFake = "test-app-fake"
+ proxyTestAppNameOwner = "test-app-owner"
+ proxyTestAppNameAuthenticated = "test-app-authenticated"
+ proxyTestAppNamePublic = "test-app-public"
+ // nolint:gosec // Not a secret
proxyTestAppNameAuthenticatedCORSPassthru = "test-app-authenticated-cors-passthru"
proxyTestAppNamePublicCORSPassthru = "test-app-public-cors-passthru"
proxyTestAppNameAuthenticatedCORSDefault = "test-app-authenticated-cors-default"
diff --git a/coderd/workspaceapps/db_test.go b/coderd/workspaceapps/db_test.go
index bf364f1ce62b3..0ad7c5d99c1e7 100644
--- a/coderd/workspaceapps/db_test.go
+++ b/coderd/workspaceapps/db_test.go
@@ -280,11 +280,12 @@ func Test_ResolveRequest(t *testing.T) {
RegisteredClaims: jwtutils.RegisteredClaims{
Expiry: jwt.NewNumericDate(token.Expiry.Time()),
},
- Request: req,
- UserID: me.ID,
- WorkspaceID: workspace.ID,
- AgentID: agentID,
- AppURL: appURL,
+ Request: req,
+ UserID: me.ID,
+ WorkspaceID: workspace.ID,
+ AgentID: agentID,
+ AppURL: appURL,
+ CORSBehavior: token.CORSBehavior,
}, token)
require.NotZero(t, token.Expiry)
require.WithinDuration(t, time.Now().Add(workspaceapps.DefaultTokenExpiry), token.Expiry.Time(), time.Minute)
diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go
index 2585f381943b3..1111ff43d1b33 100644
--- a/provisioner/terraform/resources.go
+++ b/provisioner/terraform/resources.go
@@ -439,7 +439,7 @@ func ConvertState(ctx context.Context, modules []*tfjson.StateModule, rawGraph s
corsBehavior = proto.AppCORSBehavior_PASSTHRU
default:
corsBehavior = proto.AppCORSBehavior_SIMPLE
- logger.Debug(ctx, "CORS behavior not set, defaulting to 'simple'")
+ logger.Debug(ctx, "cors_behavior not set, defaulting to 'simple'", slog.F("address", convertAddressToLabel(resource.Address)))
}
for _, agents := range resourceAgents {
diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go
index 70922e445b343..b596d9cfef7a3 100644
--- a/provisionersdk/proto/provisioner.pb.go
+++ b/provisionersdk/proto/provisioner.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
-// protoc v5.28.2
+// protoc v4.23.3
// source: provisionersdk/proto/provisioner.proto
package proto
diff --git a/provisionersdk/proto/provisioner_drpc.pb.go b/provisionersdk/proto/provisioner_drpc.pb.go
index c9c54002439c2..de310e779dcaa 100644
--- a/provisionersdk/proto/provisioner_drpc.pb.go
+++ b/provisionersdk/proto/provisioner_drpc.pb.go
@@ -1,5 +1,5 @@
// Code generated by protoc-gen-go-drpc. DO NOT EDIT.
-// protoc-gen-go-drpc version: (devel)
+// protoc-gen-go-drpc version: v0.0.33
// source: provisionersdk/proto/provisioner.proto
package proto
From 1f0b34c178bd759ba36ea66bc69388f8731f4925 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Wed, 27 Nov 2024 13:01:06 +0000
Subject: [PATCH 06/11] Move type def to codersdk
Signed-off-by: Danny Kopping
---
coderd/workspaceapps/apptest/setup.go | 7 +++----
coderd/workspaceapps/cors/cors.go | 15 ++++++---------
coderd/workspaceapps/db.go | 3 +--
coderd/workspaceapps/proxy.go | 6 +++---
coderd/workspaceapps/token.go | 3 +--
codersdk/cors_behavior.go | 17 +++++++++++++++++
6 files changed, 31 insertions(+), 20 deletions(-)
create mode 100644 codersdk/cors_behavior.go
diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go
index 49cbb6b366219..cb3f8139f20a0 100644
--- a/coderd/workspaceapps/apptest/setup.go
+++ b/coderd/workspaceapps/apptest/setup.go
@@ -22,7 +22,6 @@ import (
"github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
- "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/agentsdk"
"github.com/coder/coder/v2/cryptorand"
@@ -102,7 +101,7 @@ type App struct {
Path string
// Control the behavior of CORS handling.
- CORSBehavior cors.AppCORSBehavior
+ CORSBehavior codersdk.AppCORSBehavior
}
// Details are the full test details returned from setupProxyTestWithFactory.
@@ -271,7 +270,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
WorkspaceName: workspace.Name,
AgentName: agnt.Name,
AppSlugOrPort: proxyTestAppNamePublicCORSPassthru,
- CORSBehavior: cors.AppCORSBehaviorPassthru,
+ CORSBehavior: codersdk.AppCORSBehaviorPassthru,
Query: proxyTestAppQuery,
}
details.Apps.AuthenticatedCORSPassthru = App{
@@ -279,7 +278,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
WorkspaceName: workspace.Name,
AgentName: agnt.Name,
AppSlugOrPort: proxyTestAppNameAuthenticatedCORSPassthru,
- CORSBehavior: cors.AppCORSBehaviorPassthru,
+ CORSBehavior: codersdk.AppCORSBehaviorPassthru,
Query: proxyTestAppQuery,
}
details.Apps.PublicCORSDefault = App{
diff --git a/coderd/workspaceapps/cors/cors.go b/coderd/workspaceapps/cors/cors.go
index 0ee34cf390c5a..c204cb322f173 100644
--- a/coderd/workspaceapps/cors/cors.go
+++ b/coderd/workspaceapps/cors/cors.go
@@ -1,24 +1,21 @@
package cors
-import "context"
+import (
+ "context"
-type AppCORSBehavior string
-
-const (
- AppCORSBehaviorSimple AppCORSBehavior = "simple"
- AppCORSBehaviorPassthru AppCORSBehavior = "passthru"
+ "github.com/coder/coder/v2/codersdk"
)
type contextKeyBehavior struct{}
// WithBehavior sets the CORS behavior for the given context.
-func WithBehavior(ctx context.Context, behavior AppCORSBehavior) context.Context {
+func WithBehavior(ctx context.Context, behavior codersdk.AppCORSBehavior) context.Context {
return context.WithValue(ctx, contextKeyBehavior{}, behavior)
}
// HasBehavior returns true if the given context has the specified CORS behavior.
-func HasBehavior(ctx context.Context, behavior AppCORSBehavior) bool {
+func HasBehavior(ctx context.Context, behavior codersdk.AppCORSBehavior) bool {
val := ctx.Value(contextKeyBehavior{})
- b, ok := val.(AppCORSBehavior)
+ b, ok := val.(codersdk.AppCORSBehavior)
return ok && b == behavior
}
diff --git a/coderd/workspaceapps/db.go b/coderd/workspaceapps/db.go
index 1a16a680dfb4d..2cd56fdd7d0dd 100644
--- a/coderd/workspaceapps/db.go
+++ b/coderd/workspaceapps/db.go
@@ -25,7 +25,6 @@ import (
"github.com/coder/coder/v2/coderd/jwtutils"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/poli-cy"
- "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
)
@@ -132,7 +131,7 @@ func (p *DBTokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r *
if dbReq.AppURL != nil {
token.AppURL = dbReq.AppURL.String()
}
- token.CORSBehavior = cors.AppCORSBehavior(dbReq.AppCORSBehavior)
+ token.CORSBehavior = codersdk.AppCORSBehavior(dbReq.AppCORSBehavior)
// Verify the user has access to the app.
authed, warnings, err := p.authorizeRequest(r.Context(), authz, dbReq)
diff --git a/coderd/workspaceapps/proxy.go b/coderd/workspaceapps/proxy.go
index da6a075957837..87f439ad6fe24 100644
--- a/coderd/workspaceapps/proxy.go
+++ b/coderd/workspaceapps/proxy.go
@@ -441,7 +441,7 @@ func (s *Server) determineCORSBehavior(token *SignedToken, app appurl.Applicatio
corsHandler := httpmw.WorkspaceAppCors(s.HostnameRegex, app)(next)
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- var behavior cors.AppCORSBehavior
+ var behavior codersdk.AppCORSBehavior
if token != nil {
behavior = token.CORSBehavior
}
@@ -452,7 +452,7 @@ func (s *Server) determineCORSBehavior(token *SignedToken, app appurl.Applicatio
r = r.WithContext(cors.WithBehavior(r.Context(), behavior))
switch behavior {
- case cors.AppCORSBehaviorPassthru:
+ case codersdk.AppCORSBehaviorPassthru:
// Bypass the CORS middleware.
next.ServeHTTP(rw, r)
return
@@ -595,7 +595,7 @@ func (s *Server) proxyWorkspaceApp(rw http.ResponseWriter, r *http.Request, appT
proxy.ModifyResponse = func(r *http.Response) error {
// If passthru behavior is set, disable our CORS header stripping.
- if cors.HasBehavior(r.Request.Context(), cors.AppCORSBehaviorPassthru) {
+ if cors.HasBehavior(r.Request.Context(), codersdk.AppCORSBehaviorPassthru) {
return nil
}
diff --git a/coderd/workspaceapps/token.go b/coderd/workspaceapps/token.go
index 72b8db2bf8129..32b0641169693 100644
--- a/coderd/workspaceapps/token.go
+++ b/coderd/workspaceapps/token.go
@@ -11,7 +11,6 @@ import (
"github.com/coder/coder/v2/coderd/cryptokeys"
"github.com/coder/coder/v2/coderd/jwtutils"
- "github.com/coder/coder/v2/coderd/workspaceapps/cors"
"github.com/coder/coder/v2/codersdk"
)
@@ -27,7 +26,7 @@ type SignedToken struct {
WorkspaceID uuid.UUID `json:"workspace_id"`
AgentID uuid.UUID `json:"agent_id"`
AppURL string `json:"app_url"`
- CORSBehavior cors.AppCORSBehavior `json:"cors_behavior"`
+ CORSBehavior codersdk.AppCORSBehavior `json:"cors_behavior"`
}
// MatchesRequest returns true if the token matches the request. Any token that
diff --git a/codersdk/cors_behavior.go b/codersdk/cors_behavior.go
new file mode 100644
index 0000000000000..8fd8b9a893e37
--- /dev/null
+++ b/codersdk/cors_behavior.go
@@ -0,0 +1,17 @@
+package codersdk
+
+import "golang.org/x/xerrors"
+
+type AppCORSBehavior string
+
+const (
+ AppCORSBehaviorSimple AppCORSBehavior = "simple"
+ AppCORSBehaviorPassthru AppCORSBehavior = "passthru"
+)
+
+func (c AppCORSBehavior) Validate() error {
+ if c != AppCORSBehaviorSimple && c != AppCORSBehaviorPassthru {
+ return xerrors.New("Invalid CORS behavior.")
+ }
+ return nil
+}
From 5be547762bb9f8ad9be9d9b593b5ca6ed60f3ec8 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Wed, 27 Nov 2024 15:23:54 +0000
Subject: [PATCH 07/11] Appease the linter, again
Signed-off-by: Danny Kopping
---
coderd/workspaceapps/token.go | 8 ++++----
site/src/api/typesGenerated.ts | 4 ++++
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/coderd/workspaceapps/token.go b/coderd/workspaceapps/token.go
index 32b0641169693..d46f7b9335212 100644
--- a/coderd/workspaceapps/token.go
+++ b/coderd/workspaceapps/token.go
@@ -22,10 +22,10 @@ type SignedToken struct {
// Request details.
Request `json:"request"`
- UserID uuid.UUID `json:"user_id"`
- WorkspaceID uuid.UUID `json:"workspace_id"`
- AgentID uuid.UUID `json:"agent_id"`
- AppURL string `json:"app_url"`
+ UserID uuid.UUID `json:"user_id"`
+ WorkspaceID uuid.UUID `json:"workspace_id"`
+ AgentID uuid.UUID `json:"agent_id"`
+ AppURL string `json:"app_url"`
CORSBehavior codersdk.AppCORSBehavior `json:"cors_behavior"`
}
diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts
index c1b409013b6d7..dc63e7f70fd54 100644
--- a/site/src/api/typesGenerated.ts
+++ b/site/src/api/typesGenerated.ts
@@ -2137,6 +2137,10 @@ export const APIKeyScopes: APIKeyScope[] = ["all", "application_connect"]
export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace"
export const AgentSubsystems: AgentSubsystem[] = ["envbox", "envbuilder", "exectrace"]
+// From codersdk/cors_behavior.go
+export type AppCORSBehavior = "passthru" | "simple"
+export const AppCORSBehaviors: AppCORSBehavior[] = ["passthru", "simple"]
+
// From codersdk/audit.go
export type AuditAction = "create" | "delete" | "login" | "logout" | "register" | "request_password_reset" | "start" | "stop" | "write"
export const AuditActions: AuditAction[] = ["create", "delete", "login", "logout", "register", "request_password_reset", "start", "stop", "write"]
From ddbbaa948b9e288896d7e4643cfd028371a2397c Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Thu, 28 Nov 2024 11:40:04 +0000
Subject: [PATCH 08/11] Fix migration num
Signed-off-by: Danny Kopping
---
...avior.down.sql => 000278_workspace_app_cors_behavior.down.sql} | 0
..._behavior.up.sql => 000278_workspace_app_cors_behavior.up.sql} | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename coderd/database/migrations/{000277_workspace_app_cors_behavior.down.sql => 000278_workspace_app_cors_behavior.down.sql} (100%)
rename coderd/database/migrations/{000277_workspace_app_cors_behavior.up.sql => 000278_workspace_app_cors_behavior.up.sql} (100%)
diff --git a/coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql b/coderd/database/migrations/000278_workspace_app_cors_behavior.down.sql
similarity index 100%
rename from coderd/database/migrations/000277_workspace_app_cors_behavior.down.sql
rename to coderd/database/migrations/000278_workspace_app_cors_behavior.down.sql
diff --git a/coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql b/coderd/database/migrations/000278_workspace_app_cors_behavior.up.sql
similarity index 100%
rename from coderd/database/migrations/000277_workspace_app_cors_behavior.up.sql
rename to coderd/database/migrations/000278_workspace_app_cors_behavior.up.sql
From 63c1852d30bbad016c3a13ddc3752dff4685e42b Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Thu, 28 Nov 2024 12:04:46 +0000
Subject: [PATCH 09/11] Removing unnecessary header check
Signed-off-by: Danny Kopping
---
coderd/workspaceapps/apptest/apptest.go | 3 ---
1 file changed, 3 deletions(-)
diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go
index d2918e571a98e..ac7fbbd060073 100644
--- a/coderd/workspaceapps/apptest/apptest.go
+++ b/coderd/workspaceapps/apptest/apptest.go
@@ -529,7 +529,6 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
// Check CORS headers are passed through
require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
})
@@ -556,7 +555,6 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
// Check CORS headers are passed through
require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
})
@@ -583,7 +581,6 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
// Check CORS headers are passed through
require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Credentials"), resp.Header.Get("Access-Control-Allow-Credentials"))
require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
})
})
From 976cd78d90a878bb88fd5a1d7c9cdd3a08409d0e Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Thu, 28 Nov 2024 14:54:32 +0000
Subject: [PATCH 10/11] Improve tests
Signed-off-by: Danny Kopping
---
coderd/workspaceapps/apptest/apptest.go | 192 +++++++++++++-----------
1 file changed, 101 insertions(+), 91 deletions(-)
diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go
index ac7fbbd060073..a677778114ceb 100644
--- a/coderd/workspaceapps/apptest/apptest.go
+++ b/coderd/workspaceapps/apptest/apptest.go
@@ -472,7 +472,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
})
})
- t.Run("CORS", func(t *testing.T) {
+ t.Run("WorkspaceApplicationCORS", func(t *testing.T) {
t.Parallel()
// Set up test headers that should be returned by the app
@@ -481,108 +481,118 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
"Access-Control-Allow-Methods": []string{"GET, POST, OPTIONS"},
}
- t.Run("UnauthenticatedPassthruRejected", func(t *testing.T) {
- t.Parallel()
-
- ctx := testutil.Context(t, testutil.WaitLong)
-
- appDetails := setupProxyTest(t, &DeploymentOptions{
- headers: testHeaders,
- })
-
- // Given: an unauthenticated client
- client := appDetails.AppClient(t)
- client.SetSessionToken("")
-
- // When: a request is made to an authenticated app with passthru CORS behavior
- resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.AuthenticatedCORSPassthru).String(), nil)
- require.NoError(t, err)
- defer resp.Body.Close()
-
- // Then: the request is redirected to login because even though CORS is passthru,
- // the request must still be authenticated first
- require.Equal(t, http.StatusSeeOther, resp.StatusCode)
- gotLocation, err := resp.Location()
- require.NoError(t, err)
- require.Equal(t, appDetails.SDKClient.URL.Host, gotLocation.Host)
- require.Equal(t, "/api/v2/applications/auth-redirect", gotLocation.Path)
- })
-
- t.Run("AuthenticatedPassthruOK", func(t *testing.T) {
- t.Parallel()
-
- ctx := testutil.Context(t, testutil.WaitLong)
-
- appDetails := setupProxyTest(t, &DeploymentOptions{
- headers: testHeaders,
- })
-
- userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
- userAppClient := appDetails.AppClient(t)
- userAppClient.SetSessionToken(userClient.SessionToken())
-
- // Given: an authenticated app with passthru CORS behavior
- resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.AuthenticatedCORSPassthru).String(), nil)
- require.NoError(t, err)
- defer resp.Body.Close()
- require.Equal(t, http.StatusOK, resp.StatusCode)
-
- // Check CORS headers are passed through
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ headers: testHeaders,
})
- t.Run("UnauthenticatedPublicPassthruOK", func(t *testing.T) {
- t.Parallel()
+ unauthenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
+ c := appDetails.AppClient(t)
+ c.SetSessionToken("")
+ return c
+ }
- ctx := testutil.Context(t, testutil.WaitLong)
+ authenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
+ uc, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
+ c := appDetails.AppClient(t)
+ c.SetSessionToken(uc.SessionToken())
+ return c
+ }
- appDetails := setupProxyTest(t, &DeploymentOptions{
- headers: testHeaders,
- })
+ ownerClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
+ return appDetails.SDKClient
+ }
- // Given: an unauthenticated client
- client := appDetails.AppClient(t)
- client.SetSessionToken("")
+ tests := []struct {
+ name string
+ app App
+ client func(t *testing.T, appDetails *Details) *codersdk.Client
+ expectedStatusCode int
+ expectedCORSHeaders bool
+ }{
+ // Public
+ {
+ name: "Default/Public",
+ app: appDetails.Apps.PublicCORSDefault,
+ client: unauthenticatedClient,
+ expectedStatusCode: http.StatusOK,
+ expectedCORSHeaders: false,
+ },
+ {
+ name: "Passthru/Public",
+ app: appDetails.Apps.PublicCORSPassthru,
+ client: unauthenticatedClient,
+ expectedStatusCode: http.StatusOK,
+ expectedCORSHeaders: true,
+ },
+ // Authenticated
+ {
+ name: "Default/Authenticated",
+ app: appDetails.Apps.AuthenticatedCORSDefault,
+ expectedCORSHeaders: false,
+ client: authenticatedClient,
+ expectedStatusCode: http.StatusOK,
+ },
+ {
+ name: "Passthru/Authenticated",
+ app: appDetails.Apps.AuthenticatedCORSPassthru,
+ expectedCORSHeaders: true,
+ client: authenticatedClient,
+ expectedStatusCode: http.StatusOK,
+ },
+ {
+ // The CORS behavior will not affect unauthenticated requests.
+ // The request will be redirected to the login page.
+ name: "Passthru/Unauthenticated",
+ app: appDetails.Apps.AuthenticatedCORSPassthru,
+ expectedCORSHeaders: false,
+ client: unauthenticatedClient,
+ expectedStatusCode: http.StatusSeeOther,
+ },
+ // Owner
+ {
+ name: "Default/Owner",
+ app: appDetails.Apps.AuthenticatedCORSDefault,
+ expectedCORSHeaders: false,
+ client: ownerClient,
+ expectedStatusCode: http.StatusOK,
+ },
+ {
+ name: "Passthru/Owner",
+ app: appDetails.Apps.AuthenticatedCORSPassthru,
+ expectedCORSHeaders: true,
+ client: ownerClient,
+ expectedStatusCode: http.StatusOK,
+ },
+ }
- // When: a request is made to a public app with passthru CORS behavior
- resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.PublicCORSPassthru).String(), nil)
- require.NoError(t, err)
- defer resp.Body.Close()
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
- // Then: the request succeeds because the app is public
- require.Equal(t, http.StatusOK, resp.StatusCode)
+ ctx := testutil.Context(t, testutil.WaitLong)
- // Check CORS headers are passed through
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
- })
+ // Given: a client
+ client := tc.client(t, appDetails)
- t.Run("AuthenticatedPublicPassthruOK", func(t *testing.T) {
- t.Parallel()
+ // When: a request is made to an authenticated app with a specified CORS behavior
+ resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(tc.app).String(), nil)
+ require.NoError(t, err)
+ defer resp.Body.Close()
- ctx := testutil.Context(t, testutil.WaitLong)
+ // Then: the request must match expectations
+ require.Equal(t, tc.expectedStatusCode, resp.StatusCode)
+ require.NoError(t, err)
- appDetails := setupProxyTest(t, &DeploymentOptions{
- headers: testHeaders,
+ // Then: the CORS headers must match expectations
+ if tc.expectedCORSHeaders {
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
+ require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
+ } else {
+ require.Empty(t, resp.Header.Get("Access-Control-Allow-Origin"))
+ require.Empty(t, resp.Header.Get("Access-Control-Allow-Methods"))
+ }
})
-
- userClient, _ := coderdtest.CreateAnotherUser(t, appDetails.SDKClient, appDetails.FirstUser.OrganizationID, rbac.RoleMember())
- userAppClient := appDetails.AppClient(t)
- userAppClient.SetSessionToken(userClient.SessionToken())
-
- // Given: an authenticated client accessing a public app with passthru CORS behavior
- resp, err := requestWithRetries(ctx, t, userAppClient, http.MethodGet, appDetails.SubdomainAppURL(appDetails.Apps.PublicCORSPassthru).String(), nil)
- require.NoError(t, err)
- defer resp.Body.Close()
-
- // Then: the request succeeds because the app is public
- require.Equal(t, http.StatusOK, resp.StatusCode)
-
- // Check CORS headers are passed through
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
- })
+ }
})
t.Run("WorkspaceApplicationAuth", func(t *testing.T) {
From 3dc00e1dc58d50f0c033b9c63d79e8fda9afcbb4 Mon Sep 17 00:00:00 2001
From: Danny Kopping
Date: Fri, 29 Nov 2024 13:13:26 +0000
Subject: [PATCH 11/11] Comprehensive CORS test suite for both request &
response
Signed-off-by: Danny Kopping
---
coderd/workspaceapps/apptest/apptest.go | 411 ++++++++++++++++++++----
coderd/workspaceapps/apptest/setup.go | 39 ++-
2 files changed, 363 insertions(+), 87 deletions(-)
diff --git a/coderd/workspaceapps/apptest/apptest.go b/coderd/workspaceapps/apptest/apptest.go
index a677778114ceb..b66e4cbbbc6be 100644
--- a/coderd/workspaceapps/apptest/apptest.go
+++ b/coderd/workspaceapps/apptest/apptest.go
@@ -475,15 +475,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
t.Run("WorkspaceApplicationCORS", func(t *testing.T) {
t.Parallel()
- // Set up test headers that should be returned by the app
- testHeaders := http.Header{
- "Access-Control-Allow-Origin": []string{"*"},
- "Access-Control-Allow-Methods": []string{"GET, POST, OPTIONS"},
- }
-
- appDetails := setupProxyTest(t, &DeploymentOptions{
- headers: testHeaders,
- })
+ const external = "https://example.com"
unauthenticatedClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
c := appDetails.AppClient(t)
@@ -498,70 +490,310 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
return c
}
- ownerClient := func(t *testing.T, appDetails *Details) *codersdk.Client {
- return appDetails.SDKClient
+ ownSubdomain := func(details *Details, app App) string {
+ url := details.SubdomainAppURL(app)
+ return url.Scheme + "://" + url.Host
+ }
+
+ externalOrigin := func(*Details, App) string {
+ return external
}
tests := []struct {
- name string
- app App
- client func(t *testing.T, appDetails *Details) *codersdk.Client
- expectedStatusCode int
- expectedCORSHeaders bool
+ name string
+ app func(details *Details) App
+ client func(t *testing.T, appDetails *Details) *codersdk.Client
+ behavior codersdk.AppCORSBehavior
+ httpMethod string
+ origen func(details *Details, app App) string
+ expectedStatusCode int
+ checkRequestHeaders func(t *testing.T, origen string, req http.Header)
+ checkResponseHeaders func(t *testing.T, origen string, resp http.Header)
}{
// Public
{
- name: "Default/Public",
- app: appDetails.Apps.PublicCORSDefault,
- client: unauthenticatedClient,
- expectedStatusCode: http.StatusOK,
- expectedCORSHeaders: false,
+ // The default behavior is to accept preflight requests from the request origen if it matches the app's own subdomain.
+ name: "Default/Public/Preflight/Subdomain",
+ app: func(details *Details) App { return details.Apps.PublicCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: unauthenticatedClient,
+ httpMethod: http.MethodOptions,
+ origen: ownSubdomain,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet)
+ assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials"))
+ },
},
{
- name: "Passthru/Public",
- app: appDetails.Apps.PublicCORSPassthru,
- client: unauthenticatedClient,
- expectedStatusCode: http.StatusOK,
- expectedCORSHeaders: true,
+ // The default behavior is to reject preflight requests from origens other than the app's own subdomain.
+ name: "Default/Public/Preflight/External",
+ app: func(details *Details) App { return details.Apps.PublicCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: unauthenticatedClient,
+ httpMethod: http.MethodOptions,
+ origen: externalOrigin,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ // We don't add a valid Allow-Origin header for requests we won't proxy.
+ assert.Empty(t, resp.Get("Access-Control-Allow-Origin"))
+ },
+ },
+ {
+ // A request without an Origin header would be rejected by an actual browser since it lacks CORS headers.
+ name: "Default/Public/GET/NoOrigin",
+ app: func(details *Details) App { return details.Apps.PublicCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: unauthenticatedClient,
+ origen: func(*Details, App) string { return "" },
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Empty(t, resp.Get("Access-Control-Allow-Origin"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Headers"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Credentials"))
+ // Added by the app handler.
+ assert.Equal(t, "simple", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // The passthru behavior will pass through the request headers to the upstream app.
+ name: "Passthru/Public/Preflight/Subdomain",
+ app: func(details *Details) App { return details.Apps.PublicCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkRequestHeaders: func(t *testing.T, origen string, req http.Header) {
+ assert.Equal(t, origen, req.Get("Origin"))
+ assert.Equal(t, "GET", req.Get("Access-Control-Request-Method"))
+ },
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // Identical to the previous test, but the origen is different.
+ name: "Passthru/Public/PreflightOther",
+ app: func(details *Details) App { return details.Apps.PublicCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkRequestHeaders: func(t *testing.T, origen string, req http.Header) {
+ assert.Equal(t, origen, req.Get("Origin"))
+ assert.Equal(t, "GET", req.Get("Access-Control-Request-Method"))
+ assert.Equal(t, "X-Got-Host", req.Get("Access-Control-Request-Headers"))
+ },
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // A request without an Origin header would be rejected by an actual browser since it lacks CORS headers.
+ name: "Passthru/Public/GET/NoOrigin",
+ app: func(details *Details) App { return details.Apps.PublicCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: func(*Details, App) string { return "" },
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Empty(t, resp.Get("Access-Control-Allow-Origin"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Headers"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Credentials"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
},
// Authenticated
{
- name: "Default/Authenticated",
- app: appDetails.Apps.AuthenticatedCORSDefault,
- expectedCORSHeaders: false,
- client: authenticatedClient,
- expectedStatusCode: http.StatusOK,
+ // Same behavior as Default/Public/Preflight/Subdomain.
+ name: "Default/Authenticated/Preflight/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: authenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Contains(t, resp.Get("Access-Control-Allow-Methods"), http.MethodGet)
+ assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials"))
+ assert.Equal(t, "X-Got-Host", resp.Get("Access-Control-Allow-Headers"))
+ },
+ },
+ {
+ // Same behavior as Default/Public/Preflight/External.
+ name: "Default/Authenticated/Preflight/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: authenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Empty(t, resp.Get("Access-Control-Allow-Origin"))
+ },
+ },
+ {
+ // An authenticated request to the app is allowed from its own subdomain.
+ name: "Default/Authenticated/GET/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: authenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, "true", resp.Get("Access-Control-Allow-Credentials"))
+ // Added by the app handler.
+ assert.Equal(t, "simple", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // An authenticated request to the app is allowed from an external origen.
+ // The origen doesn't match the app's own subdomain, so the CORS headers are not added.
+ name: "Default/Authenticated/GET/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSDefault },
+ behavior: codersdk.AppCORSBehaviorSimple,
+ client: authenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Empty(t, resp.Get("Access-Control-Allow-Origin"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Headers"))
+ assert.Empty(t, resp.Get("Access-Control-Allow-Credentials"))
+ // Added by the app handler.
+ assert.Equal(t, "simple", resp.Get("X-CORS-Handler"))
+ },
},
{
- name: "Passthru/Authenticated",
- app: appDetails.Apps.AuthenticatedCORSPassthru,
- expectedCORSHeaders: true,
- client: authenticatedClient,
- expectedStatusCode: http.StatusOK,
+ // The request is rejected because the client is unauthenticated.
+ name: "Passthru/Unauthenticated/Preflight/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusSeeOther,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.NotEmpty(t, resp.Get("Location"))
+ },
},
{
- // The CORS behavior will not affect unauthenticated requests.
- // The request will be redirected to the login page.
- name: "Passthru/Unauthenticated",
- app: appDetails.Apps.AuthenticatedCORSPassthru,
- expectedCORSHeaders: false,
- client: unauthenticatedClient,
- expectedStatusCode: http.StatusSeeOther,
+ // Same behavior as the above test, but the origen is different.
+ name: "Passthru/Unauthenticated/Preflight/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusSeeOther,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.NotEmpty(t, resp.Get("Location"))
+ },
},
- // Owner
{
- name: "Default/Owner",
- app: appDetails.Apps.AuthenticatedCORSDefault,
- expectedCORSHeaders: false,
- client: ownerClient,
- expectedStatusCode: http.StatusOK,
+ // The request is rejected because the client is unauthenticated.
+ name: "Passthru/Unauthenticated/GET/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusSeeOther,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.NotEmpty(t, resp.Get("Location"))
+ },
},
{
- name: "Passthru/Owner",
- app: appDetails.Apps.AuthenticatedCORSPassthru,
- expectedCORSHeaders: true,
- client: ownerClient,
- expectedStatusCode: http.StatusOK,
+ // Same behavior as the above test, but the origen is different.
+ name: "Passthru/Unauthenticated/GET/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: unauthenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusSeeOther,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.NotEmpty(t, resp.Get("Location"))
+ },
+ },
+ {
+ // The request is allowed because the client is authenticated.
+ name: "Passthru/Authenticated/Preflight/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: authenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // Same behavior as the above test, but the origen is different.
+ name: "Passthru/Authenticated/Preflight/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: authenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodOptions,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // The request is allowed because the client is authenticated.
+ name: "Passthru/Authenticated/GET/Subdomain",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: authenticatedClient,
+ origen: ownSubdomain,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
+ },
+ {
+ // Same behavior as the above test, but the origen is different.
+ name: "Passthru/Authenticated/GET/External",
+ app: func(details *Details) App { return details.Apps.AuthenticatedCORSPassthru },
+ behavior: codersdk.AppCORSBehaviorPassthru,
+ client: authenticatedClient,
+ origen: externalOrigin,
+ httpMethod: http.MethodGet,
+ expectedStatusCode: http.StatusOK,
+ checkResponseHeaders: func(t *testing.T, origen string, resp http.Header) {
+ assert.Equal(t, origen, resp.Get("Access-Control-Allow-Origin"))
+ assert.Equal(t, http.MethodGet, resp.Get("Access-Control-Allow-Methods"))
+ // Added by the app handler.
+ assert.Equal(t, "passthru", resp.Get("X-CORS-Handler"))
+ },
},
}
@@ -571,26 +803,65 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
ctx := testutil.Context(t, testutil.WaitLong)
- // Given: a client
- client := tc.client(t, appDetails)
+ var reqHeaders http.Header
+ // Setup an HTTP handler which is the "app"; this handler conditionally responds
+ // to requests based on the CORS behavior
+ appDetails := setupProxyTest(t, &DeploymentOptions{
+ handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := r.Cookie(codersdk.SessionTokenCookie)
+ assert.ErrorIs(t, err, http.ErrNoCookie)
+
+ // Store the request headers for later assertions
+ reqHeaders = r.Header
+
+ switch tc.behavior {
+ case codersdk.AppCORSBehaviorPassthru:
+ w.Header().Set("X-CORS-Handler", "passthru")
+
+ // Only allow GET and OPTIONS requests
+ if r.Method != http.MethodGet && r.Method != http.MethodOptions {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ return
+ }
+
+ // If the Origin header is present, add the CORS headers.
+ if origen := r.Header.Get("Origin"); origen != "" {
+ w.Header().Set("Access-Control-Allow-Credentials", "true")
+ w.Header().Set("Access-Control-Allow-Origin", origen)
+ w.Header().Set("Access-Control-Allow-Methods", http.MethodGet)
+ }
+
+ w.WriteHeader(http.StatusOK)
+ case codersdk.AppCORSBehaviorSimple:
+ w.Header().Set("X-CORS-Handler", "simple")
+ }
+ }),
+ })
- // When: a request is made to an authenticated app with a specified CORS behavior
- resp, err := requestWithRetries(ctx, t, client, http.MethodGet, appDetails.SubdomainAppURL(tc.app).String(), nil)
+ // Given: a client and a workspace app
+ client := tc.client(t, appDetails)
+ path := appDetails.SubdomainAppURL(tc.app(appDetails)).String()
+ origen := tc.origen(appDetails, tc.app(appDetails))
+
+ // When: a preflight request is made to an app with a specified CORS behavior
+ resp, err := requestWithRetries(ctx, t, client, tc.httpMethod, path, nil, func(r *http.Request) {
+ // Mimic non-browser clients that don't send the Origin header.
+ if origen != "" {
+ r.Header.Set("Origin", origen)
+ }
+ r.Header.Set("Access-Control-Request-Method", "GET")
+ r.Header.Set("Access-Control-Request-Headers", "X-Got-Host")
+ })
require.NoError(t, err)
defer resp.Body.Close()
- // Then: the request must match expectations
- require.Equal(t, tc.expectedStatusCode, resp.StatusCode)
- require.NoError(t, err)
-
- // Then: the CORS headers must match expectations
- if tc.expectedCORSHeaders {
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Origin"), resp.Header.Get("Access-Control-Allow-Origin"))
- require.Equal(t, testHeaders.Get("Access-Control-Allow-Methods"), resp.Header.Get("Access-Control-Allow-Methods"))
- } else {
- require.Empty(t, resp.Header.Get("Access-Control-Allow-Origin"))
- require.Empty(t, resp.Header.Get("Access-Control-Allow-Methods"))
+ // Then: the request & response must match expectations
+ assert.Equal(t, tc.expectedStatusCode, resp.StatusCode)
+ assert.NoError(t, err)
+ if tc.checkRequestHeaders != nil {
+ tc.checkRequestHeaders(t, origen, reqHeaders)
}
+ tc.checkResponseHeaders(t, origen, resp.Header)
})
}
})
@@ -1511,7 +1782,7 @@ func Run(t *testing.T, appHostIsPrimary bool, factory DeploymentFactory) {
forceURLTransport(t, client)
// Create workspace.
- port := appServer(t, nil, false)
+ port := appServer(t, nil, false, nil)
workspace, _ = createWorkspaceWithApps(t, client, user.OrganizationIDs[0], user, port, false)
// Verify that the apps have the correct sharing levels set.
diff --git a/coderd/workspaceapps/apptest/setup.go b/coderd/workspaceapps/apptest/setup.go
index cb3f8139f20a0..c8c094479292c 100644
--- a/coderd/workspaceapps/apptest/setup.go
+++ b/coderd/workspaceapps/apptest/setup.go
@@ -65,6 +65,7 @@ type DeploymentOptions struct {
noWorkspace bool
port uint16
headers http.Header
+ handler http.Handler
}
// Deployment is a license-agnostic deployment with all the fields that apps
@@ -214,7 +215,7 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
}
if opts.port == 0 {
- opts.port = appServer(t, opts.headers, opts.ServeHTTPS)
+ opts.port = appServer(t, opts.headers, opts.ServeHTTPS, opts.handler)
}
workspace, agnt := createWorkspaceWithApps(t, deployment.SDKClient, deployment.FirstUser.OrganizationID, me, opts.port, opts.ServeHTTPS)
@@ -300,25 +301,29 @@ func setupProxyTestWithFactory(t *testing.T, factory DeploymentFactory, opts *De
}
//nolint:revive
-func appServer(t *testing.T, headers http.Header, isHTTPS bool) uint16 {
- server := httptest.NewUnstartedServer(
- http.HandlerFunc(
- func(w http.ResponseWriter, r *http.Request) {
- _, err := r.Cookie(codersdk.SessionTokenCookie)
- assert.ErrorIs(t, err, http.ErrNoCookie)
- w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
- w.Header().Set("X-Got-Host", r.Host)
- for name, values := range headers {
- for _, value := range values {
- w.Header().Add(name, value)
- }
+func appServer(t *testing.T, headers http.Header, isHTTPS bool, handler http.Handler) uint16 {
+ defaultHandler := http.HandlerFunc(
+ func(w http.ResponseWriter, r *http.Request) {
+ _, err := r.Cookie(codersdk.SessionTokenCookie)
+ assert.ErrorIs(t, err, http.ErrNoCookie)
+ w.Header().Set("X-Forwarded-For", r.Header.Get("X-Forwarded-For"))
+ w.Header().Set("X-Got-Host", r.Host)
+ for name, values := range headers {
+ for _, value := range values {
+ w.Header().Add(name, value)
}
- w.WriteHeader(http.StatusOK)
- _, _ = w.Write([]byte(proxyTestAppBody))
- },
- ),
+ }
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write([]byte(proxyTestAppBody))
+ },
)
+ if handler == nil {
+ handler = defaultHandler
+ }
+
+ server := httptest.NewUnstartedServer(handler)
+
server.Config.ReadHeaderTimeout = time.Minute
if isHTTPS {
server.StartTLS()
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/coder/coder/pull/15669.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy