From 478001a1ead2181497253d8e1b8d26b92a38fae4 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 9 May 2023 01:29:33 +0000 Subject: [PATCH 01/12] feat: add session token to provisioner --- coderd/coderd.go | 1 + .../provisionerdserver/provisionerdserver.go | 83 +++++++++++++++++++ provisioner/terraform/provision.go | 1 + provisionersdk/proto/provisioner.pb.go | 17 +++- provisionersdk/proto/provisioner.proto | 1 + 5 files changed, 100 insertions(+), 3 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index a22f632485692..8f71dfee06713 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -964,6 +964,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(ctx context.Context, debounce ti TemplateScheduleStore: api.TemplateScheduleStore, AcquireJobDebounce: debounce, Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)), + DeploymentValues: api.DeploymentValues, }) if err != nil { return nil, err diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 8c82b615172b2..5479833d4c247 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -2,10 +2,12 @@ package provisionerdserver import ( "context" + "crypto/sha256" "database/sql" "encoding/json" "errors" "fmt" + "net" "net/http" "net/url" "reflect" @@ -37,6 +39,7 @@ import ( "github.com/coder/coder/coderd/telemetry" "github.com/coder/coder/coderd/tracing" "github.com/coder/coder/codersdk" + "github.com/coder/coder/cryptorand" "github.com/coder/coder/provisioner" "github.com/coder/coder/provisionerd/proto" "github.com/coder/coder/provisionersdk" @@ -62,6 +65,7 @@ type Server struct { QuotaCommitter *atomic.Pointer[proto.QuotaCommitter] Auditor *atomic.Pointer[audit.Auditor] TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore] + DeploymentValues *codersdk.DeploymentValues AcquireJobDebounce time.Duration OIDCConfig httpmw.OAuth2Config @@ -193,6 +197,11 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac } } + sessionToken, err := server.regenerateSessionToken(ctx, owner, workspace) + if err != nil { + return nil, failJob(fmt.Sprintf("regenerate session token: %s", err)) + } + // Compute parameters for the workspace to consume. parameters, err := parameter.Compute(ctx, server.Database, parameter.ComputeScope{ TemplateImportJobID: templateVersion.JobID, @@ -286,6 +295,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac WorkspaceOwnerId: owner.ID.String(), TemplateName: template.Name, TemplateVersion: templateVersion.Name, + CoderSessionToken: sessionToken, }, LogLevel: input.LogLevel, }, @@ -1410,6 +1420,79 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. return nil } +func workspaceSessionTokenName(workspace database.Workspace) string { + return fmt.Sprintf("%s_%s_session_token", workspace.OwnerID, workspace.ID) +} + +// Generates a new ID and secret for an API key. +// TODO put API key logic in separate package. +func GenerateAPIKeyIDSecret() (id string, secret string, err error) { + // Length of an API Key ID. + id, err = cryptorand.String(10) + if err != nil { + return "", "", err + } + // Length of an API Key secret. + secret, err = cryptorand.String(22) + if err != nil { + return "", "", err + } + return id, secret, nil +} + +func (server *Server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { + id, secret, err := GenerateAPIKeyIDSecret() + if err != nil { + return "", xerrors.Errorf("generate API key: %w", err) + } + hashed := sha256.Sum256([]byte(secret)) + + err = server.Database.InTx( + func(tx database.Store) error { + key, err := tx.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ + UserID: workspace.OwnerID, + TokenName: workspaceSessionTokenName(workspace), + }) + if err == nil { + err = tx.DeleteAPIKeyByID(ctx, key.ID) + if err != nil { + return xerrors.Errorf("delete api key: %w", err) + } + } + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("get api key by name: %w", err) + } + + ip := net.IPv4(0, 0, 0, 0) + bitlen := len(ip) * 8 + _, err = tx.InsertAPIKey(ctx, database.InsertAPIKeyParams{ + ID: id, + UserID: workspace.OwnerID, + LifetimeSeconds: int64(server.DeploymentValues.SessionDuration.Value().Seconds()), + ExpiresAt: database.Now().Add(server.DeploymentValues.SessionDuration.Value()).UTC(), + IPAddress: pqtype.Inet{ + IPNet: net.IPNet{ + IP: ip, + Mask: net.CIDRMask(bitlen, bitlen), + }, + Valid: true, + }, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + HashedSecret: hashed[:], + LoginType: user.LoginType, + Scope: database.APIKeyScopeAll, + TokenName: workspaceSessionTokenName(workspace), + }) + + return nil + }, nil) + if err != nil { + return "", xerrors.Errorf("regenerate API key: %w", err) + } + return fmt.Sprintf("%s-%s", id, secret), nil +} + // obtainOIDCAccessToken returns a valid OpenID Connect access token // for the user if it's able to obtain one, otherwise it returns an empty string. func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig httpmw.OAuth2Config, userID uuid.UUID) (string, error) { diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index 3622fb13b901b..c96d9853152ce 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -220,6 +220,7 @@ func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue "CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken, "CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId, "CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId, + "CODER_SESSION_TOKEN="+config.Metadata.CoderSessionToken, ) for key, value := range provisionersdk.AgentScriptEnv() { env = append(env, key+"="+value) diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 3891af661b101..0cbcafad489fd 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -2161,6 +2161,7 @@ type Provision_Metadata struct { TemplateName string `protobuf:"bytes,8,opt,name=template_name,json=templateName,proto3" json:"template_name,omitempty"` TemplateVersion string `protobuf:"bytes,9,opt,name=template_version,json=templateVersion,proto3" json:"template_version,omitempty"` WorkspaceOwnerOidcAccessToken string `protobuf:"bytes,10,opt,name=workspace_owner_oidc_access_token,json=workspaceOwnerOidcAccessToken,proto3" json:"workspace_owner_oidc_access_token,omitempty"` + CoderSessionToken string `protobuf:"bytes,11,opt,name=coder_session_token,json=coderSessionToken,proto3" json:"coder_session_token,omitempty"` } func (x *Provision_Metadata) Reset() { @@ -2265,6 +2266,13 @@ func (x *Provision_Metadata) GetWorkspaceOwnerOidcAccessToken() string { return "" } +func (x *Provision_Metadata) GetCoderSessionToken() string { + if x != nil { + return x.CoderSessionToken + } + return "" +} + // Config represents execution configuration shared by both Plan and // Apply commands. type Provision_Config struct { @@ -3054,8 +3062,8 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x22, 0x90, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x1a, 0xeb, 0x03, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, + 0x70, 0x65, 0x22, 0xc0, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x1a, 0x9b, 0x04, 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, @@ -3085,7 +3093,10 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 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, 0x1a, 0xad, + 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2e, + 0x0a, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x64, + 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0xad, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 5f466c959e6e3..0bc8402f18ee9 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -241,6 +241,7 @@ message Provision { string template_name = 8; string template_version = 9; string workspace_owner_oidc_access_token = 10; + string coder_session_token = 11; } // Config represents execution configuration shared by both Plan and From 49fb3b576b97ed6ec19008bb34e4b24ca11bab4f Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 9 May 2023 02:04:17 +0000 Subject: [PATCH 02/12] add test --- .../provisionerdserver/provisionerdserver.go | 7 +++++-- .../provisionerdserver_test.go | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 5479833d4c247..5bdd7768a0975 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1468,8 +1468,8 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. _, err = tx.InsertAPIKey(ctx, database.InsertAPIKeyParams{ ID: id, UserID: workspace.OwnerID, - LifetimeSeconds: int64(server.DeploymentValues.SessionDuration.Value().Seconds()), - ExpiresAt: database.Now().Add(server.DeploymentValues.SessionDuration.Value()).UTC(), + LifetimeSeconds: int64(server.DeploymentValues.MaxTokenLifetime.Value().Seconds()), + ExpiresAt: database.Now().Add(server.DeploymentValues.MaxTokenLifetime.Value()).UTC(), IPAddress: pqtype.Inet{ IPNet: net.IPNet{ IP: ip, @@ -1484,6 +1484,9 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. Scope: database.APIKeyScopeAll, TokenName: workspaceSessionTokenName(workspace), }) + if err != nil { + return xerrors.Errorf("insert API key: %w", err) + } return nil }, nil) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 883b45a119e9e..058942e8f4056 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -5,6 +5,7 @@ import ( "database/sql" "encoding/json" "net/url" + "strings" "sync/atomic" "testing" "time" @@ -15,6 +16,7 @@ import ( "golang.org/x/oauth2" "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/cli/clibase" "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/dbfake" @@ -61,6 +63,7 @@ func TestAcquireJob(t *testing.T) { Auditor: mockAuditor(), TemplateScheduleStore: testTemplateScheduleStore(), Tracer: trace.NewNoopTracerProvider().Tracer("noop"), + DeploymentValues: &codersdk.DeploymentValues{}, } job, err := srv.AcquireJob(context.Background(), nil) require.NoError(t, err) @@ -102,6 +105,10 @@ func TestAcquireJob(t *testing.T) { t.Parallel() srv := setup(t, false) gitAuthProvider := "github" + // Set the max session token lifetime so we can assert we + // create an API key with an expiration within the bounds of the + // deployment config. + srv.DeploymentValues.MaxTokenLifetime = clibase.Duration(time.Hour) srv.GitAuthConfigs = []*gitauth.Config{{ ID: gitAuthProvider, OAuth2Config: &testutil.OAuth2Config{}, @@ -216,6 +223,16 @@ func TestAcquireJob(t *testing.T) { got, err := json.Marshal(job.Type) require.NoError(t, err) + // Validate that a session token is generated during the job. + sessionToken := job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.CoderSessionToken + require.NotEmpty(t, sessionToken) + toks := strings.Split(sessionToken, "-") + require.Len(t, toks, 2, "invalid api key") + key, err := srv.Database.GetAPIKeyByID(ctx, toks[0]) + require.NoError(t, err) + require.Equal(t, int64(srv.DeploymentValues.MaxTokenLifetime.Value().Seconds()), key.LifetimeSeconds) + require.WithinDuration(t, time.Now().Add(srv.DeploymentValues.MaxTokenLifetime.Value()), key.ExpiresAt, time.Minute) + want, err := json.Marshal(&proto.AcquiredJob_WorkspaceBuild_{ WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{ WorkspaceBuildId: build.ID.String(), @@ -247,6 +264,7 @@ func TestAcquireJob(t *testing.T) { WorkspaceOwnerId: user.ID.String(), TemplateName: template.Name, TemplateVersion: version.Name, + CoderSessionToken: sessionToken, }, }, }) @@ -1205,6 +1223,7 @@ func setup(t *testing.T, ignoreLogErrors bool) *provisionerdserver.Server { Auditor: mockAuditor(), TemplateScheduleStore: testTemplateScheduleStore(), Tracer: trace.NewNoopTracerProvider().Tracer("noop"), + DeploymentValues: &codersdk.DeploymentValues{}, } } From 8b52ca5ac94e4bf969466cba05d7777f0ab372fe Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 9 May 2023 17:05:13 +0000 Subject: [PATCH 03/12] fix perms --- coderd/database/dbauthz/dbauthz.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 155dbd79b2dcb..b823f7bd7067f 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -138,6 +138,7 @@ var ( rbac.ResourceUser.Type: {rbac.ActionRead}, rbac.ResourceWorkspace.Type: {rbac.ActionRead, rbac.ActionUpdate, rbac.ActionDelete}, rbac.ResourceUserData.Type: {rbac.ActionRead, rbac.ActionUpdate}, + rbac.ResourceAPIKey.Type: {rbac.WildcardSymbol}, }), Org: map[string][]rbac.Permission{}, User: []rbac.Permission{}, From b49d18b2f56b7b10d890ab9183fce43d24a1932e Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 10 May 2023 00:57:15 +0000 Subject: [PATCH 04/12] fix panic --- enterprise/coderd/provisionerdaemons.go | 1 + 1 file changed, 1 insertion(+) diff --git a/enterprise/coderd/provisionerdaemons.go b/enterprise/coderd/provisionerdaemons.go index cd8d54e433cff..0e6f2bfa5bdab 100644 --- a/enterprise/coderd/provisionerdaemons.go +++ b/enterprise/coderd/provisionerdaemons.go @@ -232,6 +232,7 @@ func (api *API) provisionerDaemonServe(rw http.ResponseWriter, r *http.Request) Logger: api.Logger.Named(fmt.Sprintf("provisionerd-%s", daemon.Name)), Tags: rawTags, Tracer: trace.NewNoopTracerProvider().Tracer("noop"), + DeploymentValues: api.DeploymentValues, }) if err != nil { _ = conn.Close(websocket.StatusInternalError, httpapi.WebsocketCloseSprintf("drpc register provisioner daemon: %s", err)) From 3ff4539be2b0380fdb53d4fa558270d56df024ab Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 10 May 2023 01:15:58 +0000 Subject: [PATCH 05/12] move api key generation to its own package --- coderd/apikey.go | 88 ++------------ coderd/apikey/apikey.go | 110 ++++++++++++++++++ .../provisionerdserver/provisionerdserver.go | 52 ++------- coderd/userauth.go | 19 +-- coderd/workspaceapps.go | 14 ++- 5 files changed, 147 insertions(+), 136 deletions(-) create mode 100644 coderd/apikey/apikey.go diff --git a/coderd/apikey.go b/coderd/apikey.go index 5c0c2a10a040d..8fb2173264d6a 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -2,9 +2,7 @@ package coderd import ( "context" - "crypto/sha256" "fmt" - "net" "net/http" "strconv" "time" @@ -12,9 +10,9 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" "github.com/moby/moby/pkg/namesgenerator" - "github.com/tabbed/pqtype" "golang.org/x/xerrors" + "github.com/coder/coder/coderd/apikey" "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/httpapi" @@ -22,7 +20,6 @@ import ( "github.com/coder/coder/coderd/rbac" "github.com/coder/coder/coderd/telemetry" "github.com/coder/coder/codersdk" - "github.com/coder/coder/cryptorand" ) // Creates a new token API key that effectively doesn't expire. @@ -83,7 +80,7 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { return } - cookie, key, err := api.createAPIKey(ctx, createAPIKeyParams{ + cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, LoginType: database.LoginTypeToken, ExpiresAt: database.Now().Add(lifeTime), @@ -127,7 +124,7 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { user := httpmw.UserParam(r) lifeTime := time.Hour * 24 * 7 - cookie, _, err := api.createAPIKey(ctx, createAPIKeyParams{ + cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ UserID: user.ID, LoginType: database.LoginTypePassword, RemoteAddr: r.RemoteAddr, @@ -359,21 +356,6 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) { ) } -// Generates a new ID and secret for an API key. -func GenerateAPIKeyIDSecret() (id string, secret string, err error) { - // Length of an API Key ID. - id, err = cryptorand.String(10) - if err != nil { - return "", "", err - } - // Length of an API Key secret. - secret, err = cryptorand.String(22) - if err != nil { - return "", "", err - } - return id, secret, nil -} - type createAPIKeyParams struct { UserID uuid.UUID RemoteAddr string @@ -401,79 +383,27 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { return nil } -func (api *API) createAPIKey(ctx context.Context, params createAPIKeyParams) (*http.Cookie, *database.APIKey, error) { - keyID, keySecret, err := GenerateAPIKeyIDSecret() +func (api *API) createAPIKey(ctx context.Context, params apikey.CreateParams) (*http.Cookie, *database.APIKey, error) { + secret, key, err := apikey.Generate(params) if err != nil { return nil, nil, xerrors.Errorf("generate API key: %w", err) } - hashed := sha256.Sum256([]byte(keySecret)) - // Default expires at to now+lifetime, or use the configured value if not - // set. - if params.ExpiresAt.IsZero() { - if params.LifetimeSeconds != 0 { - params.ExpiresAt = database.Now().Add(time.Duration(params.LifetimeSeconds) * time.Second) - } else { - params.ExpiresAt = database.Now().Add(api.DeploymentValues.SessionDuration.Value()) - params.LifetimeSeconds = int64(api.DeploymentValues.SessionDuration.Value().Seconds()) - } - } - if params.LifetimeSeconds == 0 { - params.LifetimeSeconds = int64(time.Until(params.ExpiresAt).Seconds()) - } - - ip := net.ParseIP(params.RemoteAddr) - if ip == nil { - ip = net.IPv4(0, 0, 0, 0) - } - bitlen := len(ip) * 8 - - scope := database.APIKeyScopeAll - if params.Scope != "" { - scope = params.Scope - } - switch scope { - case database.APIKeyScopeAll, database.APIKeyScopeApplicationConnect: - default: - return nil, nil, xerrors.Errorf("invalid API key scope: %q", scope) - } - - key, err := api.Database.InsertAPIKey(ctx, database.InsertAPIKeyParams{ - ID: keyID, - UserID: params.UserID, - LifetimeSeconds: params.LifetimeSeconds, - IPAddress: pqtype.Inet{ - IPNet: net.IPNet{ - IP: ip, - Mask: net.CIDRMask(bitlen, bitlen), - }, - Valid: true, - }, - // Make sure in UTC time for common time zone - ExpiresAt: params.ExpiresAt.UTC(), - CreatedAt: database.Now(), - UpdatedAt: database.Now(), - HashedSecret: hashed[:], - LoginType: params.LoginType, - Scope: scope, - TokenName: params.TokenName, - }) + newkey, err := api.Database.InsertAPIKey(ctx, key) if err != nil { return nil, nil, xerrors.Errorf("insert API key: %w", err) } api.Telemetry.Report(&telemetry.Snapshot{ - APIKeys: []telemetry.APIKey{telemetry.ConvertAPIKey(key)}, + APIKeys: []telemetry.APIKey{telemetry.ConvertAPIKey(newkey)}, }) - // This format is consumed by the APIKey middleware. - sessionToken := fmt.Sprintf("%s-%s", keyID, keySecret) return &http.Cookie{ Name: codersdk.SessionTokenCookie, - Value: sessionToken, + Value: secret, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, Secure: api.SecureAuthCookie, - }, &key, nil + }, &newkey, nil } diff --git a/coderd/apikey/apikey.go b/coderd/apikey/apikey.go new file mode 100644 index 0000000000000..41224374cb69a --- /dev/null +++ b/coderd/apikey/apikey.go @@ -0,0 +1,110 @@ +package apikey + +import ( + "crypto/sha256" + "fmt" + "net" + "time" + + "github.com/google/uuid" + "github.com/tabbed/pqtype" + "golang.org/x/xerrors" + + "github.com/coder/coder/coderd/database" + "github.com/coder/coder/codersdk" + "github.com/coder/coder/cryptorand" +) + +type CreateParams struct { + UserID uuid.UUID + LoginType database.LoginType + DeploymentValues *codersdk.DeploymentValues + + // Optional. + ExpiresAt time.Time + LifetimeSeconds int64 + Scope database.APIKeyScope + TokenName string + RemoteAddr string +} + +// Generate generates an API key, returning the key as a string as well as the +// database representation. It is the responsibility of the caller to insert it +// into the database. +func Generate(params CreateParams) (string, database.InsertAPIKeyParams, error) { + keyID, keySecret, err := generateKey() + if err != nil { + return "", database.InsertAPIKeyParams{}, xerrors.Errorf("generate API key: %w", err) + } + + hashed := sha256.Sum256([]byte(keySecret)) + + // Default expires at to now+lifetime, or use the configured value if not + // set. + if params.ExpiresAt.IsZero() { + if params.LifetimeSeconds != 0 { + params.ExpiresAt = database.Now().Add(time.Duration(params.LifetimeSeconds) * time.Second) + } else { + params.ExpiresAt = database.Now().Add(params.DeploymentValues.SessionDuration.Value()) + params.LifetimeSeconds = int64(params.DeploymentValues.SessionDuration.Value().Seconds()) + } + } + if params.LifetimeSeconds == 0 { + params.LifetimeSeconds = int64(time.Until(params.ExpiresAt).Seconds()) + } + + ip := net.ParseIP(params.RemoteAddr) + if ip == nil { + ip = net.IPv4(0, 0, 0, 0) + } + + bitlen := len(ip) * 8 + + scope := database.APIKeyScopeAll + if params.Scope != "" { + scope = params.Scope + } + switch scope { + case database.APIKeyScopeAll, database.APIKeyScopeApplicationConnect: + default: + return "", database.InsertAPIKeyParams{}, xerrors.Errorf("invalid API key scope: %q", scope) + } + + keyStr := fmt.Sprintf("%s-%s", keyID, keySecret) + + return keyStr, database.InsertAPIKeyParams{ + ID: keyID, + UserID: params.UserID, + LifetimeSeconds: params.LifetimeSeconds, + IPAddress: pqtype.Inet{ + IPNet: net.IPNet{ + IP: ip, + Mask: net.CIDRMask(bitlen, bitlen), + }, + Valid: true, + }, + // Make sure in UTC time for common time zone + ExpiresAt: params.ExpiresAt.UTC(), + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + HashedSecret: hashed[:], + LoginType: params.LoginType, + Scope: scope, + TokenName: params.TokenName, + }, nil +} + +// generateKey a new ID and secret for an API key. +func generateKey() (id string, secret string, err error) { + // Length of an API Key ID. + id, err = cryptorand.String(10) + if err != nil { + return "", "", err + } + // Length of an API Key secret. + secret, err = cryptorand.String(22) + if err != nil { + return "", "", err + } + return id, secret, nil +} diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 5bdd7768a0975..79964cb1024ae 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -2,12 +2,10 @@ package provisionerdserver import ( "context" - "crypto/sha256" "database/sql" "encoding/json" "errors" "fmt" - "net" "net/http" "net/url" "reflect" @@ -29,6 +27,7 @@ import ( "cdr.dev/slog" + "github.com/coder/coder/coderd/apikey" "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/dbauthz" @@ -39,7 +38,6 @@ import ( "github.com/coder/coder/coderd/telemetry" "github.com/coder/coder/coderd/tracing" "github.com/coder/coder/codersdk" - "github.com/coder/coder/cryptorand" "github.com/coder/coder/provisioner" "github.com/coder/coder/provisionerd/proto" "github.com/coder/coder/provisionersdk" @@ -1424,28 +1422,16 @@ func workspaceSessionTokenName(workspace database.Workspace) string { return fmt.Sprintf("%s_%s_session_token", workspace.OwnerID, workspace.ID) } -// Generates a new ID and secret for an API key. -// TODO put API key logic in separate package. -func GenerateAPIKeyIDSecret() (id string, secret string, err error) { - // Length of an API Key ID. - id, err = cryptorand.String(10) - if err != nil { - return "", "", err - } - // Length of an API Key secret. - secret, err = cryptorand.String(22) - if err != nil { - return "", "", err - } - return id, secret, nil -} - func (server *Server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { - id, secret, err := GenerateAPIKeyIDSecret() + secret, newkey, err := apikey.Generate(apikey.CreateParams{ + UserID: user.ID, + LoginType: user.LoginType, + DeploymentValues: server.DeploymentValues, + TokenName: workspaceSessionTokenName(workspace), + }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) } - hashed := sha256.Sum256([]byte(secret)) err = server.Database.InTx( func(tx database.Store) error { @@ -1463,27 +1449,7 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. return xerrors.Errorf("get api key by name: %w", err) } - ip := net.IPv4(0, 0, 0, 0) - bitlen := len(ip) * 8 - _, err = tx.InsertAPIKey(ctx, database.InsertAPIKeyParams{ - ID: id, - UserID: workspace.OwnerID, - LifetimeSeconds: int64(server.DeploymentValues.MaxTokenLifetime.Value().Seconds()), - ExpiresAt: database.Now().Add(server.DeploymentValues.MaxTokenLifetime.Value()).UTC(), - IPAddress: pqtype.Inet{ - IPNet: net.IPNet{ - IP: ip, - Mask: net.CIDRMask(bitlen, bitlen), - }, - Valid: true, - }, - CreatedAt: database.Now(), - UpdatedAt: database.Now(), - HashedSecret: hashed[:], - LoginType: user.LoginType, - Scope: database.APIKeyScopeAll, - TokenName: workspaceSessionTokenName(workspace), - }) + _, err = tx.InsertAPIKey(ctx, newkey) if err != nil { return xerrors.Errorf("insert API key: %w", err) } @@ -1493,7 +1459,7 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. if err != nil { return "", xerrors.Errorf("regenerate API key: %w", err) } - return fmt.Sprintf("%s-%s", id, secret), nil + return secret, nil } // obtainOIDCAccessToken returns a valid OpenID Connect access token diff --git a/coderd/userauth.go b/coderd/userauth.go index 5e670b79a0a55..e3ee528e23e27 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -19,6 +19,7 @@ import ( "golang.org/x/xerrors" "cdr.dev/slog" + "github.com/coder/coder/coderd/apikey" "github.com/coder/coder/coderd/audit" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/dbauthz" @@ -129,10 +130,11 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) { } //nolint:gocritic // Creating the API key as the user instead of as system. - cookie, key, err := api.createAPIKey(dbauthz.As(ctx, userSubj), createAPIKeyParams{ - UserID: user.ID, - LoginType: database.LoginTypePassword, - RemoteAddr: r.RemoteAddr, + cookie, key, err := api.createAPIKey(dbauthz.As(ctx, userSubj), apikey.CreateParams{ + UserID: user.ID, + LoginType: database.LoginTypePassword, + RemoteAddr: r.RemoteAddr, + DeploymentValues: api.DeploymentValues, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ @@ -1011,10 +1013,11 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook } //nolint:gocritic - cookie, key, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), createAPIKeyParams{ - UserID: user.ID, - LoginType: params.LoginType, - RemoteAddr: r.RemoteAddr, + cookie, key, err := api.createAPIKey(dbauthz.AsSystemRestricted(ctx), apikey.CreateParams{ + UserID: user.ID, + LoginType: params.LoginType, + DeploymentValues: api.DeploymentValues, + RemoteAddr: r.RemoteAddr, }) if err != nil { return nil, database.APIKey{}, xerrors.Errorf("create API key: %w", err) diff --git a/coderd/workspaceapps.go b/coderd/workspaceapps.go index 1f1224e028d7a..df33889467b80 100644 --- a/coderd/workspaceapps.go +++ b/coderd/workspaceapps.go @@ -11,6 +11,7 @@ import ( "golang.org/x/xerrors" + "github.com/coder/coder/coderd/apikey" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/dbauthz" "github.com/coder/coder/coderd/httpapi" @@ -109,12 +110,13 @@ func (api *API) workspaceApplicationAuth(rw http.ResponseWriter, r *http.Request exp = database.Now().Add(api.DeploymentValues.SessionDuration.Value()) lifetimeSeconds = int64(api.DeploymentValues.SessionDuration.Value().Seconds()) } - cookie, _, err := api.createAPIKey(ctx, createAPIKeyParams{ - UserID: apiKey.UserID, - LoginType: database.LoginTypePassword, - ExpiresAt: exp, - LifetimeSeconds: lifetimeSeconds, - Scope: database.APIKeyScopeApplicationConnect, + cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ + UserID: apiKey.UserID, + LoginType: database.LoginTypePassword, + DeploymentValues: api.DeploymentValues, + ExpiresAt: exp, + LifetimeSeconds: lifetimeSeconds, + Scope: database.APIKeyScopeApplicationConnect, }) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ From 01f7a5e9af6d9932aef514bd09149e935571248a Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 10 May 2023 01:25:08 +0000 Subject: [PATCH 06/12] lint --- coderd/apikey.go | 32 +++++++------------ .../provisionerdserver/provisionerdserver.go | 1 + 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/coderd/apikey.go b/coderd/apikey.go index 8fb2173264d6a..080d9d166a9d6 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -81,12 +81,13 @@ func (api *API) postToken(rw http.ResponseWriter, r *http.Request) { } cookie, key, err := api.createAPIKey(ctx, apikey.CreateParams{ - UserID: user.ID, - LoginType: database.LoginTypeToken, - ExpiresAt: database.Now().Add(lifeTime), - Scope: scope, - LifetimeSeconds: int64(lifeTime.Seconds()), - TokenName: tokenName, + UserID: user.ID, + LoginType: database.LoginTypeToken, + DeploymentValues: api.DeploymentValues, + ExpiresAt: database.Now().Add(lifeTime), + Scope: scope, + LifetimeSeconds: int64(lifeTime.Seconds()), + TokenName: tokenName, }) if err != nil { if database.IsUniqueViolation(err, database.UniqueIndexApiKeyName) { @@ -125,9 +126,10 @@ func (api *API) postAPIKey(rw http.ResponseWriter, r *http.Request) { lifeTime := time.Hour * 24 * 7 cookie, _, err := api.createAPIKey(ctx, apikey.CreateParams{ - UserID: user.ID, - LoginType: database.LoginTypePassword, - RemoteAddr: r.RemoteAddr, + UserID: user.ID, + DeploymentValues: api.DeploymentValues, + LoginType: database.LoginTypePassword, + RemoteAddr: r.RemoteAddr, // All api generated keys will last 1 week. Browser login tokens have // a shorter life. ExpiresAt: database.Now().Add(lifeTime), @@ -356,18 +358,6 @@ func (api *API) tokenConfig(rw http.ResponseWriter, r *http.Request) { ) } -type createAPIKeyParams struct { - UserID uuid.UUID - RemoteAddr string - LoginType database.LoginType - - // Optional. - ExpiresAt time.Time - LifetimeSeconds int64 - Scope database.APIKeyScope - TokenName string -} - func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { if lifetime <= 0 { return xerrors.New("lifetime must be positive number greater than 0") diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 79964cb1024ae..f5bf4ecce7d02 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1428,6 +1428,7 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. LoginType: user.LoginType, DeploymentValues: server.DeploymentValues, TokenName: workspaceSessionTokenName(workspace), + LifetimeSeconds: int64(server.DeploymentValues.MaxTokenLifetime.Value().Seconds()), }) if err != nil { return "", xerrors.Errorf("generate API key: %w", err) From 905cd1afff7fac01161353c47b16cb057ca96770 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 16 May 2023 03:23:43 +0000 Subject: [PATCH 07/12] add apikey pkg test --- coderd/apikey/apikey_test.go | 163 +++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 coderd/apikey/apikey_test.go diff --git a/coderd/apikey/apikey_test.go b/coderd/apikey/apikey_test.go new file mode 100644 index 0000000000000..c0cae7306b9bb --- /dev/null +++ b/coderd/apikey/apikey_test.go @@ -0,0 +1,163 @@ +package apikey_test + +import ( + "crypto/sha256" + "strings" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + + "github.com/coder/coder/cli/clibase" + "github.com/coder/coder/coderd/apikey" + "github.com/coder/coder/coderd/database" + "github.com/coder/coder/codersdk" +) + +func TestGenerate(t *testing.T) { + t.Parallel() + + type testcase struct { + name string + params apikey.CreateParams + fail bool + } + + cases := []testcase{ + { + name: "OK", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DeploymentValues: &codersdk.DeploymentValues{}, + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScopeApplicationConnect, + }, + }, + { + name: "InvalidScope", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DeploymentValues: &codersdk.DeploymentValues{}, + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScope("test"), + }, + fail: true, + }, + { + name: "DeploymentSessionDuration", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DeploymentValues: &codersdk.DeploymentValues{ + SessionDuration: clibase.Duration(time.Hour), + }, + LifetimeSeconds: 0, + ExpiresAt: time.Time{}, + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScopeApplicationConnect, + }, + }, + { + name: "DefaultIP", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DeploymentValues: &codersdk.DeploymentValues{}, + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "", + Scope: database.APIKeyScopeApplicationConnect, + }, + }, + { + name: "DefaultScope", + params: apikey.CreateParams{ + UserID: uuid.New(), + LoginType: database.LoginTypeOIDC, + DeploymentValues: &codersdk.DeploymentValues{}, + ExpiresAt: time.Now().Add(time.Hour), + LifetimeSeconds: int64(time.Hour.Seconds()), + TokenName: "hello", + RemoteAddr: "1.2.3.4", + Scope: database.APIKeyScopeApplicationConnect, + }, + }, + } + + for _, tc := range cases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + keystr, key, err := apikey.Generate(tc.params) + if tc.fail { + require.Error(t, err) + return + } + require.NoError(t, err) + require.NotEmpty(t, keystr) + require.NotEmpty(t, key.ID) + require.NotEmpty(t, key.HashedSecret) + + // Assert the string secret is formatted correctly + keytokens := strings.Split(keystr, "-") + require.Len(t, keytokens, 2) + require.Equal(t, key.ID, keytokens[0]) + + // Assert that the hashed secret is correct. + hashed := sha256.Sum256([]byte(keytokens[1])) + require.ElementsMatch(t, hashed, key.HashedSecret[:]) + + require.Equal(t, tc.params.UserID, key.UserID) + require.WithinDuration(t, database.Now(), key.CreatedAt, time.Second*5) + require.WithinDuration(t, database.Now(), key.UpdatedAt, time.Second*5) + + if tc.params.LifetimeSeconds > 0 { + require.Equal(t, tc.params.LifetimeSeconds, key.LifetimeSeconds) + } else if !tc.params.ExpiresAt.IsZero() { + // Should not be a delta greater than 5 seconds. + require.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5) + } else { + require.Equal(t, int64(tc.params.DeploymentValues.SessionDuration.Value().Seconds()), key.LifetimeSeconds) + } + + if !tc.params.ExpiresAt.IsZero() { + require.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt) + } else if tc.params.LifetimeSeconds > 0 { + require.WithinDuration(t, database.Now().Add(time.Duration(tc.params.LifetimeSeconds)), key.ExpiresAt, time.Second*5) + } else { + require.WithinDuration(t, database.Now().Add(tc.params.DeploymentValues.SessionDuration.Value()), key.ExpiresAt, time.Second*5) + } + + if tc.params.RemoteAddr != "" { + require.Equal(t, tc.params.RemoteAddr, key.IPAddress.IPNet.IP.String()) + } else { + require.Equal(t, "0.0.0.0", key.IPAddress.IPNet.IP.String()) + } + + if tc.params.Scope != "" { + require.Equal(t, tc.params.Scope, key.Scope) + } else { + require.Equal(t, database.APIKeyScopeAll, key.Scope) + } + + if tc.params.TokenName != "" { + require.Equal(t, tc.params.TokenName, key.TokenName) + } + if tc.params.LoginType != "" { + require.Equal(t, tc.params.LoginType, key.LoginType) + } + }) + } +} From 6ad169d80848b4b25d590992f6afcc364a304200 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 16 May 2023 04:10:22 +0000 Subject: [PATCH 08/12] update provisionerd test --- .../provisionerdserver/provisionerdserver.go | 61 +++++++++++-------- .../provisionerdserver_test.go | 59 ++++++++++++++++-- 2 files changed, 89 insertions(+), 31 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index f5bf4ecce7d02..8b6f5dc8ebdc4 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -195,9 +195,18 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac } } - sessionToken, err := server.regenerateSessionToken(ctx, owner, workspace) - if err != nil { - return nil, failJob(fmt.Sprintf("regenerate session token: %s", err)) + var sessionToken string + switch workspaceBuild.Transition { + case database.WorkspaceTransitionStart: + sessionToken, err = server.regenerateSessionToken(ctx, owner, workspace) + if err != nil { + return nil, failJob(fmt.Sprintf("regenerate session token: %s", err)) + } + case database.WorkspaceTransitionStop, database.WorkspaceTransitionDelete: + err = server.deleteSessionToken(ctx, workspace) + if err != nil { + return nil, failJob(fmt.Sprintf("delete session token: %s", err)) + } } // Compute parameters for the workspace to consume. @@ -1434,35 +1443,35 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. return "", xerrors.Errorf("generate API key: %w", err) } - err = server.Database.InTx( - func(tx database.Store) error { - key, err := tx.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ - UserID: workspace.OwnerID, - TokenName: workspaceSessionTokenName(workspace), - }) - if err == nil { - err = tx.DeleteAPIKeyByID(ctx, key.ID) - if err != nil { - return xerrors.Errorf("delete api key: %w", err) - } - } - if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - return xerrors.Errorf("get api key by name: %w", err) - } - - _, err = tx.InsertAPIKey(ctx, newkey) - if err != nil { - return xerrors.Errorf("insert API key: %w", err) - } + err = server.deleteSessionToken(ctx, workspace) + if err != nil { + return "", xerrors.Errorf("delete session token: %w", err) + } - return nil - }, nil) + _, err = server.Database.InsertAPIKey(ctx, newkey) if err != nil { - return "", xerrors.Errorf("regenerate API key: %w", err) + return "", xerrors.Errorf("insert API key: %w", err) } + return secret, nil } +func (server *Server) deleteSessionToken(ctx context.Context, workspace database.Workspace) error { + key, err := server.Database.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ + UserID: workspace.OwnerID, + TokenName: workspaceSessionTokenName(workspace), + }) + if err == nil { + err = server.Database.DeleteAPIKeyByID(ctx, key.ID) + } + + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("get api key by name: %w", err) + } + + return nil +} + // obtainOIDCAccessToken returns a valid OpenID Connect access token // for the user if it's able to obtain one, otherwise it returns an empty string. func obtainOIDCAccessToken(ctx context.Context, db database.Store, oidcConfig httpmw.OAuth2Config, userID uuid.UUID) (string, error) { diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 058942e8f4056..5a8e11d4c8b64 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -199,12 +199,16 @@ func TestAcquireJob(t *testing.T) { })), }) - published := make(chan struct{}) - closeSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) { - close(published) + startPublished := make(chan struct{}) + var closed bool + closeStartSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) { + if !closed { + close(startPublished) + closed = true + } }) require.NoError(t, err) - defer closeSubscribe() + defer closeStartSubscribe() var job *proto.AcquiredJob @@ -218,7 +222,7 @@ func TestAcquireJob(t *testing.T) { } } - <-published + <-startPublished got, err := json.Marshal(job.Type) require.NoError(t, err) @@ -271,7 +275,52 @@ func TestAcquireJob(t *testing.T) { require.NoError(t, err) require.JSONEq(t, string(want), string(got)) + + // Assert that we delete the session token whenever + // a stop is issued. + stopbuild := dbgen.WorkspaceBuild(t, srv.Database, database.WorkspaceBuild{ + WorkspaceID: workspace.ID, + BuildNumber: 2, + JobID: uuid.New(), + TemplateVersionID: version.ID, + Transition: database.WorkspaceTransitionStop, + Reason: database.BuildReasonInitiator, + }) + _ = dbgen.ProvisionerJob(t, srv.Database, database.ProvisionerJob{ + ID: stopbuild.ID, + InitiatorID: user.ID, + Provisioner: database.ProvisionerTypeEcho, + StorageMethod: database.ProvisionerStorageMethodFile, + FileID: file.ID, + Type: database.ProvisionerJobTypeWorkspaceBuild, + Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{ + WorkspaceBuildID: stopbuild.ID, + })), + }) + + stopPublished := make(chan struct{}) + closeStopSubscribe, err := srv.Pubsub.Subscribe(codersdk.WorkspaceNotifyChannel(workspace.ID), func(_ context.Context, _ []byte) { + close(stopPublished) + }) + require.NoError(t, err) + defer closeStopSubscribe() + + // Grab jobs until we find the workspace build job. There is also + // an import version job that we need to ignore. + job, err = srv.AcquireJob(ctx, nil) + require.NoError(t, err) + _, ok := job.Type.(*proto.AcquiredJob_WorkspaceBuild_) + require.True(t, ok, "acquired job not a workspace build?") + + <-stopPublished + + // Validate that a session token is deleted during a stop job. + sessionToken = job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.CoderSessionToken + require.Empty(t, sessionToken) + _, err = srv.Database.GetAPIKeyByID(ctx, key.ID) + require.ErrorIs(t, err, sql.ErrNoRows) }) + t.Run("TemplateVersionDryRun", func(t *testing.T) { t.Parallel() srv := setup(t, false) From bc6dec0415023d3909baeb373ce334f6813a6364 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Tue, 16 May 2023 04:19:19 +0000 Subject: [PATCH 09/12] fix apikey test --- coderd/apikey/apikey_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/apikey/apikey_test.go b/coderd/apikey/apikey_test.go index c0cae7306b9bb..07316af782ed5 100644 --- a/coderd/apikey/apikey_test.go +++ b/coderd/apikey/apikey_test.go @@ -90,7 +90,7 @@ func TestGenerate(t *testing.T) { LifetimeSeconds: int64(time.Hour.Seconds()), TokenName: "hello", RemoteAddr: "1.2.3.4", - Scope: database.APIKeyScopeApplicationConnect, + Scope: "", }, }, } From 1d35967bae5630c89905606ec71e89ca78a5f7d6 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Wed, 17 May 2023 00:49:48 +0000 Subject: [PATCH 10/12] pr comments --- coderd/apikey.go | 4 +- coderd/apikey/apikey.go | 12 ++--- coderd/apikey/apikey_test.go | 35 ++++++------- .../provisionerdserver/provisionerdserver.go | 51 ++++++++++++------- 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/coderd/apikey.go b/coderd/apikey.go index 080d9d166a9d6..c3934d076cbdc 100644 --- a/coderd/apikey.go +++ b/coderd/apikey.go @@ -374,7 +374,7 @@ func (api *API) validateAPIKeyLifetime(lifetime time.Duration) error { } func (api *API) createAPIKey(ctx context.Context, params apikey.CreateParams) (*http.Cookie, *database.APIKey, error) { - secret, key, err := apikey.Generate(params) + key, sessionToken, err := apikey.Generate(params) if err != nil { return nil, nil, xerrors.Errorf("generate API key: %w", err) } @@ -390,7 +390,7 @@ func (api *API) createAPIKey(ctx context.Context, params apikey.CreateParams) (* return &http.Cookie{ Name: codersdk.SessionTokenCookie, - Value: secret, + Value: sessionToken, Path: "/", HttpOnly: true, SameSite: http.SameSiteLaxMode, diff --git a/coderd/apikey/apikey.go b/coderd/apikey/apikey.go index 41224374cb69a..a5741043ab84d 100644 --- a/coderd/apikey/apikey.go +++ b/coderd/apikey/apikey.go @@ -31,10 +31,10 @@ type CreateParams struct { // Generate generates an API key, returning the key as a string as well as the // database representation. It is the responsibility of the caller to insert it // into the database. -func Generate(params CreateParams) (string, database.InsertAPIKeyParams, error) { +func Generate(params CreateParams) (database.InsertAPIKeyParams, string, error) { keyID, keySecret, err := generateKey() if err != nil { - return "", database.InsertAPIKeyParams{}, xerrors.Errorf("generate API key: %w", err) + return database.InsertAPIKeyParams{}, "", xerrors.Errorf("generate API key: %w", err) } hashed := sha256.Sum256([]byte(keySecret)) @@ -67,12 +67,12 @@ func Generate(params CreateParams) (string, database.InsertAPIKeyParams, error) switch scope { case database.APIKeyScopeAll, database.APIKeyScopeApplicationConnect: default: - return "", database.InsertAPIKeyParams{}, xerrors.Errorf("invalid API key scope: %q", scope) + return database.InsertAPIKeyParams{}, "", xerrors.Errorf("invalid API key scope: %q", scope) } - keyStr := fmt.Sprintf("%s-%s", keyID, keySecret) + token := fmt.Sprintf("%s-%s", keyID, keySecret) - return keyStr, database.InsertAPIKeyParams{ + return database.InsertAPIKeyParams{ ID: keyID, UserID: params.UserID, LifetimeSeconds: params.LifetimeSeconds, @@ -91,7 +91,7 @@ func Generate(params CreateParams) (string, database.InsertAPIKeyParams, error) LoginType: params.LoginType, Scope: scope, TokenName: params.TokenName, - }, nil + }, token, nil } // generateKey a new ID and secret for an API key. diff --git a/coderd/apikey/apikey_test.go b/coderd/apikey/apikey_test.go index 07316af782ed5..89fc3a6fade02 100644 --- a/coderd/apikey/apikey_test.go +++ b/coderd/apikey/apikey_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/google/uuid" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/coder/coder/cli/clibase" @@ -100,7 +101,7 @@ func TestGenerate(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - keystr, key, err := apikey.Generate(tc.params) + key, keystr, err := apikey.Generate(tc.params) if tc.fail { require.Error(t, err) return @@ -117,46 +118,46 @@ func TestGenerate(t *testing.T) { // Assert that the hashed secret is correct. hashed := sha256.Sum256([]byte(keytokens[1])) - require.ElementsMatch(t, hashed, key.HashedSecret[:]) + assert.ElementsMatch(t, hashed, key.HashedSecret[:]) - require.Equal(t, tc.params.UserID, key.UserID) - require.WithinDuration(t, database.Now(), key.CreatedAt, time.Second*5) - require.WithinDuration(t, database.Now(), key.UpdatedAt, time.Second*5) + assert.Equal(t, tc.params.UserID, key.UserID) + assert.WithinDuration(t, database.Now(), key.CreatedAt, time.Second*5) + assert.WithinDuration(t, database.Now(), key.UpdatedAt, time.Second*5) if tc.params.LifetimeSeconds > 0 { - require.Equal(t, tc.params.LifetimeSeconds, key.LifetimeSeconds) + assert.Equal(t, tc.params.LifetimeSeconds, key.LifetimeSeconds) } else if !tc.params.ExpiresAt.IsZero() { // Should not be a delta greater than 5 seconds. - require.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5) + assert.InDelta(t, time.Until(tc.params.ExpiresAt).Seconds(), key.LifetimeSeconds, 5) } else { - require.Equal(t, int64(tc.params.DeploymentValues.SessionDuration.Value().Seconds()), key.LifetimeSeconds) + assert.Equal(t, int64(tc.params.DeploymentValues.SessionDuration.Value().Seconds()), key.LifetimeSeconds) } if !tc.params.ExpiresAt.IsZero() { - require.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt) + assert.Equal(t, tc.params.ExpiresAt.UTC(), key.ExpiresAt) } else if tc.params.LifetimeSeconds > 0 { - require.WithinDuration(t, database.Now().Add(time.Duration(tc.params.LifetimeSeconds)), key.ExpiresAt, time.Second*5) + assert.WithinDuration(t, database.Now().Add(time.Duration(tc.params.LifetimeSeconds)), key.ExpiresAt, time.Second*5) } else { - require.WithinDuration(t, database.Now().Add(tc.params.DeploymentValues.SessionDuration.Value()), key.ExpiresAt, time.Second*5) + assert.WithinDuration(t, database.Now().Add(tc.params.DeploymentValues.SessionDuration.Value()), key.ExpiresAt, time.Second*5) } if tc.params.RemoteAddr != "" { - require.Equal(t, tc.params.RemoteAddr, key.IPAddress.IPNet.IP.String()) + assert.Equal(t, tc.params.RemoteAddr, key.IPAddress.IPNet.IP.String()) } else { - require.Equal(t, "0.0.0.0", key.IPAddress.IPNet.IP.String()) + assert.Equal(t, "0.0.0.0", key.IPAddress.IPNet.IP.String()) } if tc.params.Scope != "" { - require.Equal(t, tc.params.Scope, key.Scope) + assert.Equal(t, tc.params.Scope, key.Scope) } else { - require.Equal(t, database.APIKeyScopeAll, key.Scope) + assert.Equal(t, database.APIKeyScopeAll, key.Scope) } if tc.params.TokenName != "" { - require.Equal(t, tc.params.TokenName, key.TokenName) + assert.Equal(t, tc.params.TokenName, key.TokenName) } if tc.params.LoginType != "" { - require.Equal(t, tc.params.LoginType, key.LoginType) + assert.Equal(t, tc.params.LoginType, key.LoginType) } }) } diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 8b6f5dc8ebdc4..c64cfabea597f 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -203,7 +203,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac return nil, failJob(fmt.Sprintf("regenerate session token: %s", err)) } case database.WorkspaceTransitionStop, database.WorkspaceTransitionDelete: - err = server.deleteSessionToken(ctx, workspace) + err = deleteSessionToken(ctx, server.Database, workspace) if err != nil { return nil, failJob(fmt.Sprintf("delete session token: %s", err)) } @@ -1432,7 +1432,7 @@ func workspaceSessionTokenName(workspace database.Workspace) string { } func (server *Server) regenerateSessionToken(ctx context.Context, user database.User, workspace database.Workspace) (string, error) { - secret, newkey, err := apikey.Generate(apikey.CreateParams{ + newkey, sessionToken, err := apikey.Generate(apikey.CreateParams{ UserID: user.ID, LoginType: user.LoginType, DeploymentValues: server.DeploymentValues, @@ -1443,30 +1443,43 @@ func (server *Server) regenerateSessionToken(ctx context.Context, user database. return "", xerrors.Errorf("generate API key: %w", err) } - err = server.deleteSessionToken(ctx, workspace) - if err != nil { - return "", xerrors.Errorf("delete session token: %w", err) - } + err = server.Database.InTx(func(tx database.Store) error { + err := deleteSessionToken(ctx, tx, workspace) + if err != nil { + return xerrors.Errorf("delete session token: %w", err) + } - _, err = server.Database.InsertAPIKey(ctx, newkey) + _, err = tx.InsertAPIKey(ctx, newkey) + if err != nil { + return xerrors.Errorf("insert API key: %w", err) + } + return nil + }, nil) if err != nil { - return "", xerrors.Errorf("insert API key: %w", err) + return "", xerrors.Errorf("create API key: %w", err) } - return secret, nil + return sessionToken, nil } -func (server *Server) deleteSessionToken(ctx context.Context, workspace database.Workspace) error { - key, err := server.Database.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ - UserID: workspace.OwnerID, - TokenName: workspaceSessionTokenName(workspace), - }) - if err == nil { - err = server.Database.DeleteAPIKeyByID(ctx, key.ID) - } +func deleteSessionToken(ctx context.Context, db database.Store, workspace database.Workspace) error { + err := db.InTx(func(tx database.Store) error { + key, err := tx.GetAPIKeyByName(ctx, database.GetAPIKeyByNameParams{ + UserID: workspace.OwnerID, + TokenName: workspaceSessionTokenName(workspace), + }) + if err == nil { + err = tx.DeleteAPIKeyByID(ctx, key.ID) + } - if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - return xerrors.Errorf("get api key by name: %w", err) + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + return xerrors.Errorf("get api key by name: %w", err) + } + + return nil + }, nil) + if err != nil { + return xerrors.Errorf("in tx: %w", err) } return nil From 4183989cafa801d8add7710e50ccb91fe90445ce Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 18 May 2023 03:53:33 +0000 Subject: [PATCH 11/12] rename proto field --- .../provisionerdserver/provisionerdserver.go | 2 +- .../provisionerdserver_test.go | 6 +- provisioner/terraform/provision.go | 2 +- provisionersdk/proto/provisioner.pb.go | 213 +++++++++--------- provisionersdk/proto/provisioner.proto | 2 +- 5 files changed, 113 insertions(+), 112 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index c64cfabea597f..d760a9e269fe4 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -302,7 +302,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac WorkspaceOwnerId: owner.ID.String(), TemplateName: template.Name, TemplateVersion: templateVersion.Name, - CoderSessionToken: sessionToken, + WorkspaceOwnerSessionToken: sessionToken, }, LogLevel: input.LogLevel, }, diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 5a8e11d4c8b64..c848913fb6cbd 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -228,7 +228,7 @@ func TestAcquireJob(t *testing.T) { require.NoError(t, err) // Validate that a session token is generated during the job. - sessionToken := job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.CoderSessionToken + sessionToken := job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken require.NotEmpty(t, sessionToken) toks := strings.Split(sessionToken, "-") require.Len(t, toks, 2, "invalid api key") @@ -268,7 +268,7 @@ func TestAcquireJob(t *testing.T) { WorkspaceOwnerId: user.ID.String(), TemplateName: template.Name, TemplateVersion: version.Name, - CoderSessionToken: sessionToken, + WorkspaceOwnerSessionToken: sessionToken, }, }, }) @@ -315,7 +315,7 @@ func TestAcquireJob(t *testing.T) { <-stopPublished // Validate that a session token is deleted during a stop job. - sessionToken = job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.CoderSessionToken + sessionToken = job.Type.(*proto.AcquiredJob_WorkspaceBuild_).WorkspaceBuild.Metadata.WorkspaceOwnerSessionToken require.Empty(t, sessionToken) _, err = srv.Database.GetAPIKeyByID(ctx, key.ID) require.ErrorIs(t, err, sql.ErrNoRows) diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index c96d9853152ce..7c0fb6cb9158c 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -220,7 +220,7 @@ func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue "CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken, "CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId, "CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId, - "CODER_SESSION_TOKEN="+config.Metadata.CoderSessionToken, + "CODER_SESSION_TOKEN="+config.Metadata.WorkspaceOwnerSessionToken, ) for key, value := range provisionersdk.AgentScriptEnv() { env = append(env, key+"="+value) diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 0cbcafad489fd..adffb1339441c 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -2161,7 +2161,7 @@ type Provision_Metadata struct { TemplateName string `protobuf:"bytes,8,opt,name=template_name,json=templateName,proto3" json:"template_name,omitempty"` TemplateVersion string `protobuf:"bytes,9,opt,name=template_version,json=templateVersion,proto3" json:"template_version,omitempty"` WorkspaceOwnerOidcAccessToken string `protobuf:"bytes,10,opt,name=workspace_owner_oidc_access_token,json=workspaceOwnerOidcAccessToken,proto3" json:"workspace_owner_oidc_access_token,omitempty"` - CoderSessionToken string `protobuf:"bytes,11,opt,name=coder_session_token,json=coderSessionToken,proto3" json:"coder_session_token,omitempty"` + WorkspaceOwnerSessionToken string `protobuf:"bytes,11,opt,name=workspace_owner_session_token,json=workspaceOwnerSessionToken,proto3" json:"workspace_owner_session_token,omitempty"` } func (x *Provision_Metadata) Reset() { @@ -2266,9 +2266,9 @@ func (x *Provision_Metadata) GetWorkspaceOwnerOidcAccessToken() string { return "" } -func (x *Provision_Metadata) GetCoderSessionToken() string { +func (x *Provision_Metadata) GetWorkspaceOwnerSessionToken() string { if x != nil { - return x.CoderSessionToken + return x.WorkspaceOwnerSessionToken } return "" } @@ -3062,8 +3062,8 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x22, 0xc0, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x1a, 0x9b, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, + 0x70, 0x65, 0x22, 0xd3, 0x0d, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x1a, 0xae, 0x04, 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, @@ -3093,109 +3093,110 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 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, 0x2e, - 0x0a, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x63, 0x6f, 0x64, - 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0xad, - 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, - 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3b, 0x0a, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 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, 0x04, 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, 0x1a, 0xeb, - 0x02, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 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, 0x1a, 0xad, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1c, 0x0a, 0x09, + 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 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, 0x04, 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, 0x1a, 0xeb, 0x02, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x46, 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, + 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 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, 0x03, 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, 0x04, 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, 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, + 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, + 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, + 0x52, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x1a, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x1a, 0xb3, 0x01, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x6c, 0x61, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x46, - 0x0a, 0x10, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 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, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x50, 0x6c, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x34, 0x0a, 0x05, + 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, + 0x6c, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x1a, 0xe9, 0x01, 0x0a, 0x08, 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, 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, 0x04, - 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, 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, - 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x52, 0x0a, 0x05, - 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, - 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, - 0x1a, 0x08, 0x0a, 0x06, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x1a, 0xb3, 0x01, 0x0a, 0x07, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x6c, 0x61, - 0x6e, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x34, 0x0a, 0x05, 0x61, 0x70, 0x70, - 0x6c, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, - 0x37, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x48, 0x00, - 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x1a, 0xe9, 0x01, 0x0a, 0x08, 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, 0x2c, 0x0a, 0x12, 0x67, 0x69, - 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, 0x77, 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, 0x3d, - 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 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, - 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, 0x32, 0xa3, 0x01, 0x0a, - 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x05, - 0x50, 0x61, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, - 0x12, 0x50, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, - 0x30, 0x01, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, + 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x1a, + 0x77, 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, 0x3d, 0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x08, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 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, 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, 0x32, + 0xa3, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, + 0x42, 0x0a, 0x05, 0x50, 0x61, 0x72, 0x73, 0x65, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x30, 0x01, 0x12, 0x50, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 0bc8402f18ee9..6cb29a3a015fd 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -241,7 +241,7 @@ message Provision { string template_name = 8; string template_version = 9; string workspace_owner_oidc_access_token = 10; - string coder_session_token = 11; + string workspace_owner_session_token = 11; } // Config represents execution configuration shared by both Plan and From 0bba5438a10d3ef3232db91b5a4fc2a6c56a2e11 Mon Sep 17 00:00:00 2001 From: Jon Ayers Date: Thu, 18 May 2023 04:07:50 +0000 Subject: [PATCH 12/12] stuff --- provisioner/terraform/provision.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/terraform/provision.go b/provisioner/terraform/provision.go index 7c0fb6cb9158c..87bfd856eabf0 100644 --- a/provisioner/terraform/provision.go +++ b/provisioner/terraform/provision.go @@ -220,7 +220,7 @@ func provisionEnv(config *proto.Provision_Config, params []*proto.ParameterValue "CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken, "CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId, "CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId, - "CODER_SESSION_TOKEN="+config.Metadata.WorkspaceOwnerSessionToken, + "CODER_WORKSPACE_OWNER_SESSION_TOKEN="+config.Metadata.WorkspaceOwnerSessionToken, ) for key, value := range provisionersdk.AgentScriptEnv() { env = append(env, key+"="+value)