From 016a6a93f0fa8ad3392cd418b7d59ad1ebc120cd Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 1 Apr 2022 20:55:51 +0000 Subject: [PATCH 01/20] feat: Add user scoped git ssh keys --- coderd/coderd.go | 6 +- coderd/database/dump.sql | 13 + .../migrations/000005_gitsshkey.down.sql | 1 + .../migrations/000005_gitsshkey.up.sql | 7 + coderd/database/models.go | 8 + coderd/database/querier.go | 5 + coderd/database/queries.sql.go | 1138 +++++++++++++++++ coderd/database/query.sql | 827 ++++++++++++ coderd/gitsshkey.go | 71 + coderd/gitsshkey/ed25519.go | 125 ++ coderd/gitsshkey/gitsshkey.go | 135 ++ coderd/users.go | 35 +- codersdk/gitsshkey.go | 10 + peerbroker/proto/peerbroker.pb.go | 2 +- provisionerd/proto/provisionerd.pb.go | 2 +- provisionersdk/proto/provisioner.pb.go | 2 +- 16 files changed, 2382 insertions(+), 5 deletions(-) create mode 100644 coderd/database/migrations/000005_gitsshkey.down.sql create mode 100644 coderd/database/migrations/000005_gitsshkey.up.sql create mode 100644 coderd/database/query.sql create mode 100644 coderd/gitsshkey.go create mode 100644 coderd/gitsshkey/ed25519.go create mode 100644 coderd/gitsshkey/gitsshkey.go create mode 100644 codersdk/gitsshkey.go diff --git a/coderd/coderd.go b/coderd/coderd.go index 0eac01793eb76..d0493331724b3 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -14,6 +14,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/coderd/awsidentity" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitsshkey" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/site" @@ -30,7 +31,8 @@ type Options struct { AWSCertificates awsidentity.Certificates GoogleTokenValidator *idtoken.Validator - SecureAuthCookie bool + SecureAuthCookie bool + SSHKeygenAlgorithm gitsshkey.SSHKeygenAlgorithm } // New constructs the Coder API into an HTTP handler. @@ -137,6 +139,8 @@ func New(options *Options) (http.Handler, func()) { r.Get("/", api.workspacesByUser) r.Get("/{workspacename}", api.workspaceByUserAndName) }) + r.Get("/gitsshkey", api.getGitSSHKey) + r.Post("/gitsshkey", api.regenerateGitSSHKey) }) }) }) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 3ee32b5f98d07..2573d8628ab7b 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -89,6 +89,14 @@ CREATE TABLE files ( data bytea NOT NULL ); +CREATE TABLE git_ssh_keys ( + user_id text NOT NULL, + created_at timestamp with time zone NOT NULL, + updated_at timestamp with time zone NOT NULL, + private_key bytea NOT NULL, + public_key bytea NOT NULL +); + CREATE TABLE licenses ( id integer NOT NULL, license jsonb NOT NULL, @@ -291,6 +299,11 @@ ALTER TABLE ONLY organization_members ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); +ALTER TABLE ONLY git_ssh_keys + ADD CONSTRAINT git_ssh_keys_pkey PRIMARY KEY (user_id); + +ALTER TABLE ONLY parameter_schemas + ADD CONSTRAINT parameter_schemas_id_key UNIQUE (id); ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); diff --git a/coderd/database/migrations/000005_gitsshkey.down.sql b/coderd/database/migrations/000005_gitsshkey.down.sql new file mode 100644 index 0000000000000..e54f051f85310 --- /dev/null +++ b/coderd/database/migrations/000005_gitsshkey.down.sql @@ -0,0 +1 @@ +DROP TABLE git_ssh_keys; diff --git a/coderd/database/migrations/000005_gitsshkey.up.sql b/coderd/database/migrations/000005_gitsshkey.up.sql new file mode 100644 index 0000000000000..eaf1edd6d4f18 --- /dev/null +++ b/coderd/database/migrations/000005_gitsshkey.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS git_ssh_keys ( + user_id text PRIMARY KEY NOT NULL, + created_at timestamptz NOT NULL, + updated_at timestamptz NOT NULL, + private_key bytea NOT NULL, + public_key bytea NOT NULL +); diff --git a/coderd/database/models.go b/coderd/database/models.go index 7ecf5e7d0d58c..25fefcc69b64e 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -255,6 +255,14 @@ type File struct { Data []byte `db:"data" json:"data"` } +type GitSshKey struct { + UserID string `db:"user_id" json:"user_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + PrivateKey []byte `db:"private_key" json:"private_key"` + PublicKey []byte `db:"public_key" json:"public_key"` +} + type License struct { ID int32 `db:"id" json:"id"` License json.RawMessage `db:"license" json:"license"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index abaed68e8049a..8c29155bcb31f 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -10,10 +10,13 @@ import ( type querier interface { AcquireProvisionerJob(ctx context.Context, arg AcquireProvisionerJobParams) (ProvisionerJob, error) + DeleteGitSSHKey(ctx context.Context, userID string) error DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) GetFileByHash(ctx context.Context, hash string) (File, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) + GetGitSSHKey(ctx context.Context, userID string) (GitSshKey, error) + GetOrganizationByID(ctx context.Context, id string) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error) @@ -54,6 +57,7 @@ type querier interface { GetWorkspacesByUserID(ctx context.Context, arg GetWorkspacesByUserIDParams) ([]Workspace, error) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) + InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) @@ -69,6 +73,7 @@ type querier interface { InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error + UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error UpdateProjectActiveVersionByID(ctx context.Context, arg UpdateProjectActiveVersionByIDParams) error UpdateProjectDeletedByID(ctx context.Context, arg UpdateProjectDeletedByIDParams) error UpdateProjectVersionByID(ctx context.Context, arg UpdateProjectVersionByIDParams) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 49f0669143f38..c99208c785d42 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -84,6 +84,23 @@ VALUES $14, $15 ) RETURNING id, hashed_secret, user_id, application, name, last_used, expires_at, created_at, updated_at, login_type, oidc_access_token, oidc_refresh_token, oidc_id_token, oidc_expiry, devurl_token +const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec +DELETE FROM + git_ssh_keys +WHERE + user_id = $1 +` + +func (q *sqlQuerier) DeleteGitSSHKey(ctx context.Context, userID string) error { + _, err := q.db.ExecContext(ctx, deleteGitSSHKey, userID) + return err +} + +const deleteParameterValueByID = `-- name: DeleteParameterValueByID :exec +DELETE FROM + parameter_values +WHERE + id = $1 ` type InsertAPIKeyParams struct { @@ -201,6 +218,7 @@ func (q *sqlQuerier) GetFileByHash(ctx context.Context, hash string) (File, erro return i, err } +<<<<<<< HEAD:coderd/database/queries.sql.go const insertFile = `-- name: InsertFile :one INSERT INTO files (hash, created_at, created_by, mimetype, "data") @@ -231,11 +249,35 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File &i.CreatedBy, &i.Mimetype, &i.Data, +======= +const getGitSSHKey = `-- name: GetGitSSHKey :one +SELECT + user_id, created_at, updated_at, private_key, public_key +FROM + git_ssh_keys +WHERE + user_id = $1 +` + +func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID string) (GitSshKey, error) { + row := q.db.QueryRowContext(ctx, getGitSSHKey, userID) + var i GitSshKey + err := row.Scan( + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + &i.PrivateKey, + &i.PublicKey, +>>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go ) return i, err } +<<<<<<< HEAD:coderd/database/queries.sql.go const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one +======= +const getOrganizationByID = `-- name: GetOrganizationByID :one +>>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go SELECT user_id, organization_id, created_at, updated_at, roles FROM @@ -2627,10 +2669,90 @@ VALUES type InsertWorkspaceParams struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` +<<<<<<< HEAD:coderd/database/queries.sql.go UpdatedAt time.Time `db:"updated_at" json:"updated_at"` OwnerID uuid.UUID `db:"owner_id" json:"owner_id"` ProjectID uuid.UUID `db:"project_id" json:"project_id"` Name string `db:"name" json:"name"` +======= + CreatedBy string `db:"created_by" json:"created_by"` + Mimetype string `db:"mimetype" json:"mimetype"` + Data []byte `db:"data" json:"data"` +} + +func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) { + row := q.db.QueryRowContext(ctx, insertFile, + arg.Hash, + arg.CreatedAt, + arg.CreatedBy, + arg.Mimetype, + arg.Data, + ) + var i File + err := row.Scan( + &i.Hash, + &i.CreatedAt, + &i.CreatedBy, + &i.Mimetype, + &i.Data, + ) + return i, err +} + +const insertGitSSHKey = `-- name: InsertGitSSHKey :one +INSERT INTO + git_ssh_keys ( + user_id, + created_at, + updated_at, + private_key, + public_key + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING user_id, created_at, updated_at, private_key, public_key +` + +type InsertGitSSHKeyParams struct { + UserID string `db:"user_id" json:"user_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + PrivateKey []byte `db:"private_key" json:"private_key"` + PublicKey []byte `db:"public_key" json:"public_key"` +} + +func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) { + row := q.db.QueryRowContext(ctx, insertGitSSHKey, + arg.UserID, + arg.CreatedAt, + arg.UpdatedAt, + arg.PrivateKey, + arg.PublicKey, + ) + var i GitSshKey + err := row.Scan( + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + &i.PrivateKey, + &i.PublicKey, + ) + return i, err +} + +const insertOrganization = `-- name: InsertOrganization :one +INSERT INTO + organizations (id, name, description, created_at, updated_at) +VALUES + ($1, $2, $3, $4, $5) RETURNING id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off +` + +type InsertOrganizationParams struct { + ID string `db:"id" json:"id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +>>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go } func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) { @@ -2655,6 +2777,1022 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar return i, err } +<<<<<<< HEAD:coderd/database/queries.sql.go +======= +const insertOrganizationMember = `-- name: InsertOrganizationMember :one +INSERT INTO + organization_members ( + organization_id, + user_id, + created_at, + updated_at, + roles + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING organization_id, user_id, created_at, updated_at, roles +` + +type InsertOrganizationMemberParams struct { + OrganizationID string `db:"organization_id" json:"organization_id"` + UserID string `db:"user_id" json:"user_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Roles []string `db:"roles" json:"roles"` +} + +func (q *sqlQuerier) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) { + row := q.db.QueryRowContext(ctx, insertOrganizationMember, + arg.OrganizationID, + arg.UserID, + arg.CreatedAt, + arg.UpdatedAt, + pq.Array(arg.Roles), + ) + var i OrganizationMember + err := row.Scan( + &i.OrganizationID, + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + pq.Array(&i.Roles), + ) + return i, err +} + +const insertParameterSchema = `-- name: InsertParameterSchema :one +INSERT INTO + parameter_schemas ( + id, + created_at, + job_id, + name, + description, + default_source_scheme, + default_source_value, + allow_override_source, + default_destination_scheme, + allow_override_destination, + default_refresh, + redisplay_value, + validation_error, + validation_condition, + validation_type_system, + validation_value_type + ) +VALUES + ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13, + $14, + $15, + $16 + ) RETURNING id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type +` + +type InsertParameterSchemaParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` + DefaultSourceValue string `db:"default_source_value" json:"default_source_value"` + AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` + DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` + AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` + DefaultRefresh string `db:"default_refresh" json:"default_refresh"` + RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` + ValidationError string `db:"validation_error" json:"validation_error"` + ValidationCondition string `db:"validation_condition" json:"validation_condition"` + ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` + ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` +} + +func (q *sqlQuerier) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) { + row := q.db.QueryRowContext(ctx, insertParameterSchema, + arg.ID, + arg.CreatedAt, + arg.JobID, + arg.Name, + arg.Description, + arg.DefaultSourceScheme, + arg.DefaultSourceValue, + arg.AllowOverrideSource, + arg.DefaultDestinationScheme, + arg.AllowOverrideDestination, + arg.DefaultRefresh, + arg.RedisplayValue, + arg.ValidationError, + arg.ValidationCondition, + arg.ValidationTypeSystem, + arg.ValidationValueType, + ) + var i ParameterSchema + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Name, + &i.Description, + &i.DefaultSourceScheme, + &i.DefaultSourceValue, + &i.AllowOverrideSource, + &i.DefaultDestinationScheme, + &i.AllowOverrideDestination, + &i.DefaultRefresh, + &i.RedisplayValue, + &i.ValidationError, + &i.ValidationCondition, + &i.ValidationTypeSystem, + &i.ValidationValueType, + ) + return i, err +} + +const insertParameterValue = `-- name: InsertParameterValue :one +INSERT INTO + parameter_values ( + id, + name, + created_at, + updated_at, + scope, + scope_id, + source_scheme, + source_value, + destination_scheme + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, created_at, updated_at, scope, scope_id, name, source_scheme, source_value, destination_scheme +` + +type InsertParameterValueParams struct { + ID uuid.UUID `db:"id" json:"id"` + Name string `db:"name" json:"name"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Scope ParameterScope `db:"scope" json:"scope"` + ScopeID string `db:"scope_id" json:"scope_id"` + SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"` + SourceValue string `db:"source_value" json:"source_value"` + DestinationScheme ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"` +} + +func (q *sqlQuerier) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) { + row := q.db.QueryRowContext(ctx, insertParameterValue, + arg.ID, + arg.Name, + arg.CreatedAt, + arg.UpdatedAt, + arg.Scope, + arg.ScopeID, + arg.SourceScheme, + arg.SourceValue, + arg.DestinationScheme, + ) + var i ParameterValue + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.Scope, + &i.ScopeID, + &i.Name, + &i.SourceScheme, + &i.SourceValue, + &i.DestinationScheme, + ) + return i, err +} + +const insertProject = `-- name: InsertProject :one +INSERT INTO + projects ( + id, + created_at, + updated_at, + organization_id, + name, + provisioner, + active_version_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id +` + +type InsertProjectParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID string `db:"organization_id" json:"organization_id"` + Name string `db:"name" json:"name"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` +} + +func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) { + row := q.db.QueryRowContext(ctx, insertProject, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.OrganizationID, + arg.Name, + arg.Provisioner, + arg.ActiveVersionID, + ) + var i Project + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.OrganizationID, + &i.Deleted, + &i.Name, + &i.Provisioner, + &i.ActiveVersionID, + ) + return i, err +} + +const insertProjectVersion = `-- name: InsertProjectVersion :one +INSERT INTO + project_versions ( + id, + project_id, + organization_id, + created_at, + updated_at, + name, + description, + job_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, project_id, organization_id, created_at, updated_at, name, description, job_id +` + +type InsertProjectVersionParams struct { + ID uuid.UUID `db:"id" json:"id"` + ProjectID uuid.NullUUID `db:"project_id" json:"project_id"` + OrganizationID string `db:"organization_id" json:"organization_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` + JobID uuid.UUID `db:"job_id" json:"job_id"` +} + +func (q *sqlQuerier) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) { + row := q.db.QueryRowContext(ctx, insertProjectVersion, + arg.ID, + arg.ProjectID, + arg.OrganizationID, + arg.CreatedAt, + arg.UpdatedAt, + arg.Name, + arg.Description, + arg.JobID, + ) + var i ProjectVersion + err := row.Scan( + &i.ID, + &i.ProjectID, + &i.OrganizationID, + &i.CreatedAt, + &i.UpdatedAt, + &i.Name, + &i.Description, + &i.JobID, + ) + return i, err +} + +const insertProvisionerDaemon = `-- name: InsertProvisionerDaemon :one +INSERT INTO + provisioner_daemons (id, created_at, organization_id, name, provisioners) +VALUES + ($1, $2, $3, $4, $5) RETURNING id, created_at, updated_at, organization_id, name, provisioners +` + +type InsertProvisionerDaemonParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + OrganizationID sql.NullString `db:"organization_id" json:"organization_id"` + Name string `db:"name" json:"name"` + Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"` +} + +func (q *sqlQuerier) InsertProvisionerDaemon(ctx context.Context, arg InsertProvisionerDaemonParams) (ProvisionerDaemon, error) { + row := q.db.QueryRowContext(ctx, insertProvisionerDaemon, + arg.ID, + arg.CreatedAt, + arg.OrganizationID, + arg.Name, + pq.Array(arg.Provisioners), + ) + var i ProvisionerDaemon + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.OrganizationID, + &i.Name, + pq.Array(&i.Provisioners), + ) + return i, err +} + +const insertProvisionerJob = `-- name: InsertProvisionerJob :one +INSERT INTO + provisioner_jobs ( + id, + created_at, + updated_at, + organization_id, + initiator_id, + provisioner, + storage_method, + storage_source, + type, + input + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id +` + +type InsertProvisionerJobParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OrganizationID string `db:"organization_id" json:"organization_id"` + InitiatorID string `db:"initiator_id" json:"initiator_id"` + Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` + StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"` + StorageSource string `db:"storage_source" json:"storage_source"` + Type ProvisionerJobType `db:"type" json:"type"` + Input json.RawMessage `db:"input" json:"input"` +} + +func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) { + row := q.db.QueryRowContext(ctx, insertProvisionerJob, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.OrganizationID, + arg.InitiatorID, + arg.Provisioner, + arg.StorageMethod, + arg.StorageSource, + arg.Type, + arg.Input, + ) + var i ProvisionerJob + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.StartedAt, + &i.CanceledAt, + &i.CompletedAt, + &i.Error, + &i.OrganizationID, + &i.InitiatorID, + &i.Provisioner, + &i.StorageMethod, + &i.StorageSource, + &i.Type, + &i.Input, + &i.WorkerID, + ) + return i, err +} + +const insertProvisionerJobLogs = `-- name: InsertProvisionerJobLogs :many +INSERT INTO + provisioner_job_logs +SELECT + unnest($1 :: uuid [ ]) AS id, + $2 :: uuid AS job_id, + unnest($3 :: timestamptz [ ]) AS created_at, + unnest($4 :: log_source [ ]) as source, + unnest($5 :: log_level [ ]) as level, + unnest($6 :: varchar(128) [ ]) as stage, + unnest($7 :: varchar(1024) [ ]) as output RETURNING id, job_id, created_at, source, level, stage, output +` + +type InsertProvisionerJobLogsParams struct { + ID []uuid.UUID `db:"id" json:"id"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + CreatedAt []time.Time `db:"created_at" json:"created_at"` + Source []LogSource `db:"source" json:"source"` + Level []LogLevel `db:"level" json:"level"` + Stage []string `db:"stage" json:"stage"` + Output []string `db:"output" json:"output"` +} + +func (q *sqlQuerier) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) { + rows, err := q.db.QueryContext(ctx, insertProvisionerJobLogs, + pq.Array(arg.ID), + arg.JobID, + pq.Array(arg.CreatedAt), + pq.Array(arg.Source), + pq.Array(arg.Level), + pq.Array(arg.Stage), + pq.Array(arg.Output), + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ProvisionerJobLog + for rows.Next() { + var i ProvisionerJobLog + if err := rows.Scan( + &i.ID, + &i.JobID, + &i.CreatedAt, + &i.Source, + &i.Level, + &i.Stage, + &i.Output, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertUser = `-- name: InsertUser :one +INSERT INTO + users ( + id, + email, + name, + login_type, + revoked, + hashed_password, + created_at, + updated_at, + username + ) +VALUES + ($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, temporary_password, avatar_hash, ssh_key_regenerated_at, username, dotfiles_git_uri, roles, status, relatime, gpg_key_regenerated_at, _decomissioned, shell +` + +type InsertUserParams struct { + ID string `db:"id" json:"id"` + Email string `db:"email" json:"email"` + Name string `db:"name" json:"name"` + LoginType LoginType `db:"login_type" json:"login_type"` + HashedPassword []byte `db:"hashed_password" json:"hashed_password"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Username string `db:"username" json:"username"` +} + +func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) { + row := q.db.QueryRowContext(ctx, insertUser, + arg.ID, + arg.Email, + arg.Name, + arg.LoginType, + arg.HashedPassword, + arg.CreatedAt, + arg.UpdatedAt, + arg.Username, + ) + var i User + err := row.Scan( + &i.ID, + &i.Email, + &i.Name, + &i.Revoked, + &i.LoginType, + &i.HashedPassword, + &i.CreatedAt, + &i.UpdatedAt, + &i.TemporaryPassword, + &i.AvatarHash, + &i.SshKeyRegeneratedAt, + &i.Username, + &i.DotfilesGitUri, + pq.Array(&i.Roles), + &i.Status, + &i.Relatime, + &i.GpgKeyRegeneratedAt, + &i.Decomissioned, + &i.Shell, + ) + return i, err +} + +const insertWorkspace = `-- name: InsertWorkspace :one +INSERT INTO + workspaces ( + id, + created_at, + updated_at, + owner_id, + project_id, + name + ) +VALUES + ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, updated_at, owner_id, project_id, deleted, name +` + +type InsertWorkspaceParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + OwnerID string `db:"owner_id" json:"owner_id"` + ProjectID uuid.UUID `db:"project_id" json:"project_id"` + Name string `db:"name" json:"name"` +} + +func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) { + row := q.db.QueryRowContext(ctx, insertWorkspace, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.OwnerID, + arg.ProjectID, + arg.Name, + ) + var i Workspace + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.OwnerID, + &i.ProjectID, + &i.Deleted, + &i.Name, + ) + return i, err +} + +const insertWorkspaceAgent = `-- name: InsertWorkspaceAgent :one +INSERT INTO + workspace_agents ( + id, + created_at, + updated_at, + resource_id, + auth_token, + auth_instance_id, + environment_variables, + startup_script, + instance_metadata, + resource_metadata + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, environment_variables, startup_script, instance_metadata, resource_metadata +` + +type InsertWorkspaceAgentParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` + AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` + AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` + EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` + StartupScript sql.NullString `db:"startup_script" json:"startup_script"` + InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` + ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` +} + +func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) { + row := q.db.QueryRowContext(ctx, insertWorkspaceAgent, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.ResourceID, + arg.AuthToken, + arg.AuthInstanceID, + arg.EnvironmentVariables, + arg.StartupScript, + arg.InstanceMetadata, + arg.ResourceMetadata, + ) + var i WorkspaceAgent + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.FirstConnectedAt, + &i.LastConnectedAt, + &i.DisconnectedAt, + &i.ResourceID, + &i.AuthToken, + &i.AuthInstanceID, + &i.EnvironmentVariables, + &i.StartupScript, + &i.InstanceMetadata, + &i.ResourceMetadata, + ) + return i, err +} + +const insertWorkspaceBuild = `-- name: InsertWorkspaceBuild :one +INSERT INTO + workspace_builds ( + id, + created_at, + updated_at, + workspace_id, + project_version_id, + before_id, + name, + transition, + initiator, + job_id, + provisioner_state + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id +` + +type InsertWorkspaceBuildParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` + ProjectVersionID uuid.UUID `db:"project_version_id" json:"project_version_id"` + BeforeID uuid.NullUUID `db:"before_id" json:"before_id"` + Name string `db:"name" json:"name"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Initiator string `db:"initiator" json:"initiator"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` +} + +func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) { + row := q.db.QueryRowContext(ctx, insertWorkspaceBuild, + arg.ID, + arg.CreatedAt, + arg.UpdatedAt, + arg.WorkspaceID, + arg.ProjectVersionID, + arg.BeforeID, + arg.Name, + arg.Transition, + arg.Initiator, + arg.JobID, + arg.ProvisionerState, + ) + var i WorkspaceBuild + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.UpdatedAt, + &i.WorkspaceID, + &i.ProjectVersionID, + &i.Name, + &i.BeforeID, + &i.AfterID, + &i.Transition, + &i.Initiator, + &i.ProvisionerState, + &i.JobID, + ) + return i, err +} + +const insertWorkspaceResource = `-- name: InsertWorkspaceResource :one +INSERT INTO + workspace_resources ( + id, + created_at, + job_id, + transition, + address, + type, + name, + agent_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at, job_id, transition, address, type, name, agent_id +` + +type InsertWorkspaceResourceParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + JobID uuid.UUID `db:"job_id" json:"job_id"` + Transition WorkspaceTransition `db:"transition" json:"transition"` + Address string `db:"address" json:"address"` + Type string `db:"type" json:"type"` + Name string `db:"name" json:"name"` + AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"` +} + +func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) { + row := q.db.QueryRowContext(ctx, insertWorkspaceResource, + arg.ID, + arg.CreatedAt, + arg.JobID, + arg.Transition, + arg.Address, + arg.Type, + arg.Name, + arg.AgentID, + ) + var i WorkspaceResource + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.JobID, + &i.Transition, + &i.Address, + &i.Type, + &i.Name, + &i.AgentID, + ) + return i, err +} + +const updateAPIKeyByID = `-- name: UpdateAPIKeyByID :exec +UPDATE + api_keys +SET + last_used = $2, + expires_at = $3, + oidc_access_token = $4, + oidc_refresh_token = $5, + oidc_expiry = $6 +WHERE + id = $1 +` + +type UpdateAPIKeyByIDParams struct { + ID string `db:"id" json:"id"` + LastUsed time.Time `db:"last_used" json:"last_used"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + OIDCAccessToken string `db:"oidc_access_token" json:"oidc_access_token"` + OIDCRefreshToken string `db:"oidc_refresh_token" json:"oidc_refresh_token"` + OIDCExpiry time.Time `db:"oidc_expiry" json:"oidc_expiry"` +} + +func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error { + _, err := q.db.ExecContext(ctx, updateAPIKeyByID, + arg.ID, + arg.LastUsed, + arg.ExpiresAt, + arg.OIDCAccessToken, + arg.OIDCRefreshToken, + arg.OIDCExpiry, + ) + return err +} + +const updateGitSSHKey = `-- name: UpdateGitSSHKey :exec +UPDATE + git_ssh_keys +SET + updated_at = $2, + private_key = $3, + public_key = $4 +WHERE + user_id = $1 +` + +type UpdateGitSSHKeyParams struct { + UserID string `db:"user_id" json:"user_id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + PrivateKey []byte `db:"private_key" json:"private_key"` + PublicKey []byte `db:"public_key" json:"public_key"` +} + +func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error { + _, err := q.db.ExecContext(ctx, updateGitSSHKey, + arg.UserID, + arg.UpdatedAt, + arg.PrivateKey, + arg.PublicKey, + ) + return err +} + +const updateProjectActiveVersionByID = `-- name: UpdateProjectActiveVersionByID :exec +UPDATE + projects +SET + active_version_id = $2 +WHERE + id = $1 +` + +type UpdateProjectActiveVersionByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` +} + +func (q *sqlQuerier) UpdateProjectActiveVersionByID(ctx context.Context, arg UpdateProjectActiveVersionByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProjectActiveVersionByID, arg.ID, arg.ActiveVersionID) + return err +} + +const updateProjectDeletedByID = `-- name: UpdateProjectDeletedByID :exec +UPDATE + projects +SET + deleted = $2 +WHERE + id = $1 +` + +type UpdateProjectDeletedByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + Deleted bool `db:"deleted" json:"deleted"` +} + +func (q *sqlQuerier) UpdateProjectDeletedByID(ctx context.Context, arg UpdateProjectDeletedByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProjectDeletedByID, arg.ID, arg.Deleted) + return err +} + +const updateProjectVersionByID = `-- name: UpdateProjectVersionByID :exec +UPDATE + project_versions +SET + project_id = $2, + updated_at = $3 +WHERE + id = $1 +` + +type UpdateProjectVersionByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + ProjectID uuid.NullUUID `db:"project_id" json:"project_id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +func (q *sqlQuerier) UpdateProjectVersionByID(ctx context.Context, arg UpdateProjectVersionByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProjectVersionByID, arg.ID, arg.ProjectID, arg.UpdatedAt) + return err +} + +const updateProvisionerDaemonByID = `-- name: UpdateProvisionerDaemonByID :exec +UPDATE + provisioner_daemons +SET + updated_at = $2, + provisioners = $3 +WHERE + id = $1 +` + +type UpdateProvisionerDaemonByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` + Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"` +} + +func (q *sqlQuerier) UpdateProvisionerDaemonByID(ctx context.Context, arg UpdateProvisionerDaemonByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProvisionerDaemonByID, arg.ID, arg.UpdatedAt, pq.Array(arg.Provisioners)) + return err +} + +const updateProvisionerJobByID = `-- name: UpdateProvisionerJobByID :exec +UPDATE + provisioner_jobs +SET + updated_at = $2 +WHERE + id = $1 +` + +type UpdateProvisionerJobByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` +} + +func (q *sqlQuerier) UpdateProvisionerJobByID(ctx context.Context, arg UpdateProvisionerJobByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProvisionerJobByID, arg.ID, arg.UpdatedAt) + return err +} + +const updateProvisionerJobWithCancelByID = `-- name: UpdateProvisionerJobWithCancelByID :exec +UPDATE + provisioner_jobs +SET + canceled_at = $2 +WHERE + id = $1 +` + +type UpdateProvisionerJobWithCancelByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + CanceledAt sql.NullTime `db:"canceled_at" json:"canceled_at"` +} + +func (q *sqlQuerier) UpdateProvisionerJobWithCancelByID(ctx context.Context, arg UpdateProvisionerJobWithCancelByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProvisionerJobWithCancelByID, arg.ID, arg.CanceledAt) + return err +} + +const updateProvisionerJobWithCompleteByID = `-- name: UpdateProvisionerJobWithCompleteByID :exec +UPDATE + provisioner_jobs +SET + updated_at = $2, + completed_at = $3, + error = $4 +WHERE + id = $1 +` + +type UpdateProvisionerJobWithCompleteByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"` + Error sql.NullString `db:"error" json:"error"` +} + +func (q *sqlQuerier) UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error { + _, err := q.db.ExecContext(ctx, updateProvisionerJobWithCompleteByID, + arg.ID, + arg.UpdatedAt, + arg.CompletedAt, + arg.Error, + ) + return err +} + +const updateWorkspaceAgentConnectionByID = `-- name: UpdateWorkspaceAgentConnectionByID :exec +UPDATE + workspace_agents +SET + first_connected_at = $2, + last_connected_at = $3, + disconnected_at = $4 +WHERE + id = $1 +` + +type UpdateWorkspaceAgentConnectionByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + FirstConnectedAt sql.NullTime `db:"first_connected_at" json:"first_connected_at"` + LastConnectedAt sql.NullTime `db:"last_connected_at" json:"last_connected_at"` + DisconnectedAt sql.NullTime `db:"disconnected_at" json:"disconnected_at"` +} + +func (q *sqlQuerier) UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error { + _, err := q.db.ExecContext(ctx, updateWorkspaceAgentConnectionByID, + arg.ID, + arg.FirstConnectedAt, + arg.LastConnectedAt, + arg.DisconnectedAt, + ) + return err +} + +const updateWorkspaceBuildByID = `-- name: UpdateWorkspaceBuildByID :exec +UPDATE + workspace_builds +SET + updated_at = $2, + after_id = $3, + provisioner_state = $4 +WHERE + id = $1 +` + +type UpdateWorkspaceBuildByIDParams struct { + ID uuid.UUID `db:"id" json:"id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + AfterID uuid.NullUUID `db:"after_id" json:"after_id"` + ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` +} + +func (q *sqlQuerier) UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) error { + _, err := q.db.ExecContext(ctx, updateWorkspaceBuildByID, + arg.ID, + arg.UpdatedAt, + arg.AfterID, + arg.ProvisionerState, + ) + return err +} + +>>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go const updateWorkspaceDeletedByID = `-- name: UpdateWorkspaceDeletedByID :exec UPDATE workspaces diff --git a/coderd/database/query.sql b/coderd/database/query.sql new file mode 100644 index 0000000000000..cbd5a5ac9bb21 --- /dev/null +++ b/coderd/database/query.sql @@ -0,0 +1,827 @@ +-- Database queries are generated using sqlc. See: +-- https://docs.sqlc.dev/en/latest/tutorials/getting-started-postgresql.html +-- +-- Run "make gen" to generate models and query functions. +; + +-- Acquires the lock for a single job that isn't started, completed, +-- canceled, and that matches an array of provisioner types. +-- +-- SKIP LOCKED is used to jump over locked rows. This prevents +-- multiple provisioners from acquiring the same jobs. See: +-- https://www.postgresql.org/docs/9.5/sql-select.html#SQL-FOR-UPDATE-SHARE +-- name: AcquireProvisionerJob :one +UPDATE + provisioner_jobs +SET + started_at = @started_at, + updated_at = @started_at, + worker_id = @worker_id +WHERE + id = ( + SELECT + id + FROM + provisioner_jobs AS nested + WHERE + nested.started_at IS NULL + AND nested.canceled_at IS NULL + AND nested.completed_at IS NULL + AND nested.provisioner = ANY(@types :: provisioner_type [ ]) + ORDER BY + nested.created_at FOR + UPDATE + SKIP LOCKED + LIMIT + 1 + ) RETURNING *; + +-- name: DeleteParameterValueByID :exec +DELETE FROM + parameter_values +WHERE + id = $1; + +-- name: GetAPIKeyByID :one +SELECT + * +FROM + api_keys +WHERE + id = $1 +LIMIT + 1; + +-- name: GetFileByHash :one +SELECT + * +FROM + files +WHERE + hash = $1 +LIMIT + 1; + +-- name: GetUserByID :one +SELECT + * +FROM + users +WHERE + id = $1 +LIMIT + 1; + +-- name: GetUserByEmailOrUsername :one +SELECT + * +FROM + users +WHERE + LOWER(username) = LOWER(@username) + OR email = @email +LIMIT + 1; + +-- name: GetUserCount :one +SELECT + COUNT(*) +FROM + users; + +-- name: GetOrganizationByID :one +SELECT + * +FROM + organizations +WHERE + id = $1; + +-- name: GetOrganizationByName :one +SELECT + * +FROM + organizations +WHERE + LOWER(name) = LOWER(@name) +LIMIT + 1; + +-- name: GetOrganizationsByUserID :many +SELECT + * +FROM + organizations +WHERE + id = ( + SELECT + organization_id + FROM + organization_members + WHERE + user_id = $1 + ); + +-- name: GetOrganizationMemberByUserID :one +SELECT + * +FROM + organization_members +WHERE + organization_id = $1 + AND user_id = $2 +LIMIT + 1; + +-- name: GetParameterValuesByScope :many +SELECT + * +FROM + parameter_values +WHERE + scope = $1 + AND scope_id = $2; + +-- name: GetParameterValueByScopeAndName :one +SELECT + * +FROM + parameter_values +WHERE + scope = $1 + AND scope_id = $2 + AND name = $3 +LIMIT + 1; + +-- name: GetProjectByID :one +SELECT + * +FROM + projects +WHERE + id = $1 +LIMIT + 1; + +-- name: GetProjectsByIDs :many +SELECT + * +FROM + projects +WHERE + id = ANY(@ids :: uuid [ ]); + +-- name: GetProjectByOrganizationAndName :one +SELECT + * +FROM + projects +WHERE + organization_id = @organization_id + AND deleted = @deleted + AND LOWER(name) = LOWER(@name) +LIMIT + 1; + +-- name: GetProjectsByOrganization :many +SELECT + * +FROM + projects +WHERE + organization_id = $1 + AND deleted = $2; + +-- name: GetParameterSchemasByJobID :many +SELECT + * +FROM + parameter_schemas +WHERE + job_id = $1; + +-- name: GetProjectVersionsByProjectID :many +SELECT + * +FROM + project_versions +WHERE + project_id = $1 :: uuid; + +-- name: GetProjectVersionByJobID :one +SELECT + * +FROM + project_versions +WHERE + job_id = $1; + +-- name: GetProjectVersionByProjectIDAndName :one +SELECT + * +FROM + project_versions +WHERE + project_id = $1 + AND name = $2; + +-- name: GetProjectVersionByID :one +SELECT + * +FROM + project_versions +WHERE + id = $1; + +-- name: GetProvisionerLogsByIDBetween :many +SELECT + * +FROM + provisioner_job_logs +WHERE + job_id = @job_id + AND ( + created_at >= @created_after + OR created_at <= @created_before + ) +ORDER BY + created_at; + +-- name: GetProvisionerDaemonByID :one +SELECT + * +FROM + provisioner_daemons +WHERE + id = $1; + +-- name: GetProvisionerDaemons :many +SELECT + * +FROM + provisioner_daemons; + +-- name: GetWorkspaceAgentByAuthToken :one +SELECT + * +FROM + workspace_agents +WHERE + auth_token = $1 +ORDER BY + created_at DESC; + +-- name: GetWorkspaceAgentByInstanceID :one +SELECT + * +FROM + workspace_agents +WHERE + auth_instance_id = @auth_instance_id :: text +ORDER BY + created_at DESC; + +-- name: GetProvisionerJobByID :one +SELECT + * +FROM + provisioner_jobs +WHERE + id = $1; + +-- name: GetProvisionerJobsByIDs :many +SELECT + * +FROM + provisioner_jobs +WHERE + id = ANY(@ids :: uuid [ ]); + +-- name: GetWorkspaceByID :one +SELECT + * +FROM + workspaces +WHERE + id = $1 +LIMIT + 1; + +-- name: GetWorkspacesByProjectID :many +SELECT + * +FROM + workspaces +WHERE + project_id = $1 + AND deleted = $2; + +-- name: GetWorkspacesByUserID :many +SELECT + * +FROM + workspaces +WHERE + owner_id = $1 + AND deleted = $2; + +-- name: GetWorkspaceByUserIDAndName :one +SELECT + * +FROM + workspaces +WHERE + owner_id = @owner_id + AND deleted = @deleted + AND LOWER(name) = LOWER(@name); + +-- name: GetWorkspaceOwnerCountsByProjectIDs :many +SELECT + project_id, + COUNT(DISTINCT owner_id) +FROM + workspaces +WHERE + project_id = ANY(@ids :: uuid [ ]) +GROUP BY + project_id, + owner_id; + +-- name: GetWorkspaceBuildByID :one +SELECT + * +FROM + workspace_builds +WHERE + id = $1 +LIMIT + 1; + +-- name: GetWorkspaceBuildByJobID :one +SELECT + * +FROM + workspace_builds +WHERE + job_id = $1 +LIMIT + 1; + +-- name: GetWorkspaceBuildByWorkspaceIDAndName :one +SELECT + * +FROM + workspace_builds +WHERE + workspace_id = $1 + AND name = $2; + +-- name: GetWorkspaceBuildByWorkspaceID :many +SELECT + * +FROM + workspace_builds +WHERE + workspace_id = $1; + +-- name: GetWorkspaceBuildByWorkspaceIDWithoutAfter :one +SELECT + * +FROM + workspace_builds +WHERE + workspace_id = $1 + AND after_id IS NULL +LIMIT + 1; + +-- name: GetWorkspaceBuildsByWorkspaceIDsWithoutAfter :many +SELECT + * +FROM + workspace_builds +WHERE + workspace_id = ANY(@ids :: uuid [ ]) + AND after_id IS NULL; + +-- name: GetWorkspaceResourceByID :one +SELECT + * +FROM + workspace_resources +WHERE + id = $1; + +-- name: GetWorkspaceResourcesByJobID :many +SELECT + * +FROM + workspace_resources +WHERE + job_id = $1; + +-- name: GetWorkspaceAgentByResourceID :one +SELECT + * +FROM + workspace_agents +WHERE + resource_id = $1; + +-- name: InsertAPIKey :one +INSERT INTO + api_keys ( + id, + hashed_secret, + user_id, + application, + name, + last_used, + expires_at, + created_at, + updated_at, + login_type, + oidc_access_token, + oidc_refresh_token, + oidc_id_token, + oidc_expiry, + devurl_token + ) +VALUES + ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13, + $14, + $15 + ) RETURNING *; + +-- name: InsertFile :one +INSERT INTO + files (hash, created_at, created_by, mimetype, data) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: InsertProvisionerJobLogs :many +INSERT INTO + provisioner_job_logs +SELECT + unnest(@id :: uuid [ ]) AS id, + @job_id :: uuid AS job_id, + unnest(@created_at :: timestamptz [ ]) AS created_at, + unnest(@source :: log_source [ ]) as source, + unnest(@level :: log_level [ ]) as level, + unnest(@stage :: varchar(128) [ ]) as stage, + unnest(@output :: varchar(1024) [ ]) as output RETURNING *; + +-- name: InsertOrganization :one +INSERT INTO + organizations (id, name, description, created_at, updated_at) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: InsertOrganizationMember :one +INSERT INTO + organization_members ( + organization_id, + user_id, + created_at, + updated_at, + roles + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: InsertParameterValue :one +INSERT INTO + parameter_values ( + id, + name, + created_at, + updated_at, + scope, + scope_id, + source_scheme, + source_value, + destination_scheme + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *; + +-- name: InsertProject :one +INSERT INTO + projects ( + id, + created_at, + updated_at, + organization_id, + name, + provisioner, + active_version_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7) RETURNING *; + +-- name: InsertWorkspaceResource :one +INSERT INTO + workspace_resources ( + id, + created_at, + job_id, + transition, + address, + type, + name, + agent_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; + +-- name: InsertProjectVersion :one +INSERT INTO + project_versions ( + id, + project_id, + organization_id, + created_at, + updated_at, + name, + description, + job_id + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; + +-- name: InsertParameterSchema :one +INSERT INTO + parameter_schemas ( + id, + created_at, + job_id, + name, + description, + default_source_scheme, + default_source_value, + allow_override_source, + default_destination_scheme, + allow_override_destination, + default_refresh, + redisplay_value, + validation_error, + validation_condition, + validation_type_system, + validation_value_type + ) +VALUES + ( + $1, + $2, + $3, + $4, + $5, + $6, + $7, + $8, + $9, + $10, + $11, + $12, + $13, + $14, + $15, + $16 + ) RETURNING *; + +-- name: InsertProvisionerDaemon :one +INSERT INTO + provisioner_daemons (id, created_at, organization_id, name, provisioners) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: InsertProvisionerJob :one +INSERT INTO + provisioner_jobs ( + id, + created_at, + updated_at, + organization_id, + initiator_id, + provisioner, + storage_method, + storage_source, + type, + input + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; + +-- name: InsertUser :one +INSERT INTO + users ( + id, + email, + name, + login_type, + revoked, + hashed_password, + created_at, + updated_at, + username + ) +VALUES + ($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING *; + +-- name: InsertWorkspace :one +INSERT INTO + workspaces ( + id, + created_at, + updated_at, + owner_id, + project_id, + name + ) +VALUES + ($1, $2, $3, $4, $5, $6) RETURNING *; + +-- name: InsertWorkspaceAgent :one +INSERT INTO + workspace_agents ( + id, + created_at, + updated_at, + resource_id, + auth_token, + auth_instance_id, + environment_variables, + startup_script, + instance_metadata, + resource_metadata + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; + +-- name: InsertWorkspaceBuild :one +INSERT INTO + workspace_builds ( + id, + created_at, + updated_at, + workspace_id, + project_version_id, + before_id, + name, + transition, + initiator, + job_id, + provisioner_state + ) +VALUES + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *; + +-- name: UpdateAPIKeyByID :exec +UPDATE + api_keys +SET + last_used = $2, + expires_at = $3, + oidc_access_token = $4, + oidc_refresh_token = $5, + oidc_expiry = $6 +WHERE + id = $1; + +-- name: UpdateProjectActiveVersionByID :exec +UPDATE + projects +SET + active_version_id = $2 +WHERE + id = $1; + +-- name: UpdateProjectDeletedByID :exec +UPDATE + projects +SET + deleted = $2 +WHERE + id = $1; + +-- name: UpdateProjectVersionByID :exec +UPDATE + project_versions +SET + project_id = $2, + updated_at = $3 +WHERE + id = $1; + +-- name: UpdateProvisionerDaemonByID :exec +UPDATE + provisioner_daemons +SET + updated_at = $2, + provisioners = $3 +WHERE + id = $1; + +-- name: UpdateProvisionerJobByID :exec +UPDATE + provisioner_jobs +SET + updated_at = $2 +WHERE + id = $1; + +-- name: UpdateProvisionerJobWithCancelByID :exec +UPDATE + provisioner_jobs +SET + canceled_at = $2 +WHERE + id = $1; + +-- name: UpdateProvisionerJobWithCompleteByID :exec +UPDATE + provisioner_jobs +SET + updated_at = $2, + completed_at = $3, + error = $4 +WHERE + id = $1; + +-- name: UpdateWorkspaceDeletedByID :exec +UPDATE + workspaces +SET + deleted = $2 +WHERE + id = $1; + +-- name: UpdateWorkspaceAgentConnectionByID :exec +UPDATE + workspace_agents +SET + first_connected_at = $2, + last_connected_at = $3, + disconnected_at = $4 +WHERE + id = $1; + +-- name: UpdateWorkspaceBuildByID :exec +UPDATE + workspace_builds +SET + updated_at = $2, + after_id = $3, + provisioner_state = $4 +WHERE + id = $1; + +-- name: InsertGitSSHKey :one +INSERT INTO + git_ssh_keys ( + user_id, + created_at, + updated_at, + private_key, + public_key + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: GetGitSSHKey :one +SELECT + * +FROM + git_ssh_keys +WHERE + user_id = $1; + +-- name: UpdateGitSSHKey :exec +UPDATE + git_ssh_keys +SET + updated_at = $2, + private_key = $3, + public_key = $4 +WHERE + user_id = $1; + +-- name: DeleteGitSSHKey :exec +DELETE FROM + git_ssh_keys +WHERE + user_id = $1; diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go new file mode 100644 index 0000000000000..46f1547d8aa3f --- /dev/null +++ b/coderd/gitsshkey.go @@ -0,0 +1,71 @@ +package coderd + +import ( + "net/http" + + "github.com/go-chi/render" + + "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitsshkey" + "github.com/coder/coder/coderd/httpapi" + "github.com/coder/coder/coderd/httpmw" + "github.com/coder/coder/codersdk" +) + +func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { + var ( + user = httpmw.UserParam(r) + ) + + privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Could not regenerate key pair.", + }) + return + } + + err = api.Database.UpdateGitSSHKey(r.Context(), database.UpdateGitSSHKeyParams{ + UserID: user.ID, + UpdatedAt: database.Now(), + PrivateKey: privateKey, + PublicKey: publicKey, + }) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Could not update git ssh key.", + }) + return + } + + httpapi.Write(rw, http.StatusOK, httpapi.Response{ + Message: "Updated git ssh key!", + }) +} + +func (api *api) getGitSSHKey(rw http.ResponseWriter, r *http.Request) { + var ( + user = httpmw.UserParam(r) + ) + + gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Could not update git ssh key.", + }) + return + } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, codersdk.GitSSHKey{ + UserID: gitSSHKey.UserID, + CreatedAt: gitSSHKey.CreatedAt, + UpdatedAt: gitSSHKey.UpdatedAt, + // No need to return the private key to the user + PublicKey: gitSSHKey.PublicKey, + }) +} + +func (api *api) getPrivateGitSSHKey(rw http.ResponseWriter, r *http.Request) { + // connect agent to workspace to user to gitsshkey +} diff --git a/coderd/gitsshkey/ed25519.go b/coderd/gitsshkey/ed25519.go new file mode 100644 index 0000000000000..076027d8c45a1 --- /dev/null +++ b/coderd/gitsshkey/ed25519.go @@ -0,0 +1,125 @@ +// This file contains an adapted version of the original implementation available +// under the following URL: https://github.com/mikesmitty/edkey/blob/3356ea4e686a1d47ae5d2d4c3cbc1832ce2df626/edkey.go + +// The following changes have been made: +// * Replaced usage of math/rand with crypto/rand + +// This should be removed soon as support for marshaling ED25519 private keys +// is added to the Golang standard library. +// See: https://github.com/golang/go/issues/37132 + +// --- BEGIN ORIGINAL LICENSE --- +// MIT License + +// Copyright (c) 2017 Michael Smith + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// --- END ORIGINAL LICENSE --- + +package gitsshkey + +import ( + "crypto/rand" + "encoding/binary" + + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh" + "golang.org/x/xerrors" +) + +func MarshalED25519PrivateKey(key ed25519.PrivateKey) ([]byte, error) { + // Add our key header (followed by a null byte) + magic := append([]byte("openssh-key-v1"), 0) + + var msg struct { + CipherName string + KdfName string + KdfOpts string + NumKeys uint32 + PubKey []byte + PrivKeyBlock []byte + } + + // Fill out the private key fields + pk1 := struct { + Check1 uint32 + Check2 uint32 + Keytype string + Pub []byte + Priv []byte + Comment string + Pad []byte `ssh:"rest"` + }{} + + // Random check bytes + var check uint32 + if err := binary.Read(rand.Reader, binary.BigEndian, &check); err != nil { + return nil, xerrors.Errorf("generate random bytes: %w", err) + } + + pk1.Check1 = check + pk1.Check2 = check + + // Set our key type + pk1.Keytype = ssh.KeyAlgoED25519 + + // Add the pubkey to the optionally-encrypted block + pk, ok := key.Public().(ed25519.PublicKey) + if !ok { + return nil, xerrors.Errorf("ed25519.PublicKey type assertion failed on an ed25519 public key") + } + pubKey := []byte(pk) + pk1.Pub = pubKey + + // Add our private key + pk1.Priv = []byte(key) + + // Might be useful to put something in here at some point + pk1.Comment = "" + + // Add some padding to match the encryption block size within PrivKeyBlock (without Pad field) + // 8 doesn't match the documentation, but that's what ssh-keygen uses for unencrypted keys. *shrug* + bs := 8 + blockLen := len(ssh.Marshal(pk1)) + padLen := (bs - (blockLen % bs)) % bs + pk1.Pad = make([]byte, padLen) + + // Padding is a sequence of bytes like: 1, 2, 3... + for i := 0; i < padLen; i++ { + pk1.Pad[i] = byte(i + 1) + } + + // Generate the pubkey prefix "\0\0\0\nssh-ed25519\0\0\0 " + prefix := []byte{0x0, 0x0, 0x0, 0x0b} + prefix = append(prefix, []byte(ssh.KeyAlgoED25519)...) + prefix = append(prefix, []byte{0x0, 0x0, 0x0, 0x20}...) + prefix = append(prefix, pubKey...) + + // Only going to support unencrypted keys for now + msg.CipherName = "none" + msg.KdfName = "none" + msg.KdfOpts = "" + msg.NumKeys = 1 + msg.PubKey = prefix + msg.PrivKeyBlock = ssh.Marshal(pk1) + + magic = append(magic, ssh.Marshal(msg)...) + + return magic, nil +} diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go new file mode 100644 index 0000000000000..36247e1e0064b --- /dev/null +++ b/coderd/gitsshkey/gitsshkey.go @@ -0,0 +1,135 @@ +package gitsshkey + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "strings" + + "golang.org/x/xerrors" +) + +type SSHKeygenAlgorithm string + +const ( + // SSHKeygenAlgorithmEd25519 is the Edwards-curve Digital Signature Algorithm using Curve25519 + SSHKeygenAlgorithmEd25519 SSHKeygenAlgorithm = "ed25519" + // SSHKeygenAlgorithmECDSA is the Digital Signature Algorithm (DSA) using NIST Elliptic Curve + SSHKeygenAlgorithmECDSA SSHKeygenAlgorithm = "ecdsa" + // SSHKeygenAlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm + // and creates a key with a fixed size of 4096-bit. + SSHKeygenAlgorithmRSA4096 SSHKeygenAlgorithm = "rsa4096" +) + +func GenerateKeyPair(algo SSHKeygenAlgorithm) ([]byte, []byte, error) { + switch algo { + case SSHKeygenAlgorithmEd25519: + return ed25519KeyGen() + case SSHKeygenAlgorithmECDSA: + return ecdsaKeyGen() + case SSHKeygenAlgorithmRSA4096: + return rsa4096KeyGen() + default: + return nil, nil, xerrors.Errorf("invalid SSHKeygenAlgorithm: %s", algo) + } +} + +// ed25519KeyGen returns an ED25519-based SSH private key. +func ed25519KeyGen() ([]byte, []byte, error) { + const blockType = "OPENSSH PRIVATE KEY" + + publicKey, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, nil, xerrors.Errorf("generate ed25519 private key: %w", err) + } + + // NOTE: as of the time of writing, x/crypto/ssh is unable to marshal an ED25519 private key + // into the format expected by OpenSSH. See: https://github.com/golang/go/issues/37132 + // Until this support is added, using a third-party implementation. + byt, err := MarshalED25519PrivateKey(privateKeyRaw) + if err != nil { + return nil, nil, xerrors.Errorf("marshal ed25519 private key: %w", err) + } + + pb := pem.Block{ + Type: blockType, + Headers: nil, + Bytes: byt, + } + privateKey := pem.EncodeToMemory(&pb) + + return privateKey, publicKey, nil +} + +// ecdsaKeyGen returns an ECDSA-based SSH private key. +func ecdsaKeyGen() ([]byte, []byte, error) { + const blockType = "EC PRIVATE KEY" + + privateKeyRaw, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, nil, xerrors.Errorf("generate ecdsa private key: %w", err) + } + publicKey, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) + if err != nil { + return nil, nil, xerrors.Errorf("generate RSA4096 public key: %w", err) + } + + byt, err := x509.MarshalECPrivateKey(privateKeyRaw) + if err != nil { + return nil, nil, xerrors.Errorf("marshal private key: %w", err) + } + + pb := pem.Block{ + Type: blockType, + Headers: nil, + Bytes: byt, + } + privateKey := pem.EncodeToMemory(&pb) + + return privateKey, publicKey, nil +} + +// rsaKeyGen returns an RSA-based SSH private key of size 4096. +// +// Administrators may configure this for SSH key compatibility with Azure DevOps. +func rsa4096KeyGen() ([]byte, []byte, error) { + const blockType = "RSA PRIVATE KEY" + + privateKeyRaw, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return nil, nil, xerrors.Errorf("generate RSA4096 private key: %w", err) + } + publicKey, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) + if err != nil { + return nil, nil, xerrors.Errorf("generate RSA4096 public key: %w", err) + } + + pb := pem.Block{ + Type: blockType, + Bytes: x509.MarshalPKCS1PrivateKey(privateKeyRaw), + } + privateKey := pem.EncodeToMemory(&pb) + + return privateKey, publicKey, nil +} + +// ParseSSHKeygenAlgorithm returns a valid SSHKeygenAlgorithm or error if input is not a valid. +func ParseSSHKeygenAlgorithm(t string) (SSHKeygenAlgorithm, error) { + ok := []string{ + string(SSHKeygenAlgorithmEd25519), + string(SSHKeygenAlgorithmECDSA), + string(SSHKeygenAlgorithmRSA4096), + } + + for _, a := range ok { + if string(t) == a { + return SSHKeygenAlgorithm(a), nil + } + } + + return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join([]string(ok), ",")) +} diff --git a/coderd/users.go b/coderd/users.go index 336d9091ce733..223fe64d3c29b 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -16,6 +16,7 @@ import ( "golang.org/x/xerrors" "github.com/coder/coder/coderd/database" + "github.com/coder/coder/coderd/gitsshkey" "github.com/coder/coder/coderd/httpapi" "github.com/coder/coder/coderd/httpmw" "github.com/coder/coder/coderd/userpassword" @@ -80,7 +81,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) { // Create the user, organization, and membership to the user. var user database.User var organization database.Organization - err = api.Database.InTx(func(s database.Store) error { + err = api.Database.InTx(func(db database.Store) error { user, err = api.Database.InsertUser(r.Context(), database.InsertUserParams{ ID: uuid.New(), Email: createUser.Email, @@ -93,6 +94,22 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) { if err != nil { return xerrors.Errorf("create user: %w", err) } + + privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + if err != nil { + return xerrors.Errorf("generate user gitsshkey: %w", err) + } + _, err = db.InsertGitSSHKey(r.Context(), database.InsertGitSSHKeyParams{ + UserID: user.ID, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + PrivateKey: privateKey, + PublicKey: publicKey, + }) + if err != nil { + return xerrors.Errorf("insert user gitsshkey: %w", err) + } + organization, err = api.Database.InsertOrganization(r.Context(), database.InsertOrganizationParams{ ID: uuid.New(), Name: createUser.OrganizationName, @@ -206,6 +223,22 @@ func (api *api) postUsers(rw http.ResponseWriter, r *http.Request) { if err != nil { return xerrors.Errorf("create user: %w", err) } + + privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + if err != nil { + return xerrors.Errorf("generate user gitsshkey: %w", err) + } + _, err = db.InsertGitSSHKey(r.Context(), database.InsertGitSSHKeyParams{ + UserID: user.ID, + CreatedAt: database.Now(), + UpdatedAt: database.Now(), + PrivateKey: privateKey, + PublicKey: publicKey, + }) + if err != nil { + return xerrors.Errorf("insert user gitsshkey: %w", err) + } + _, err = db.InsertOrganizationMember(r.Context(), database.InsertOrganizationMemberParams{ OrganizationID: organization.ID, UserID: user.ID, diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go new file mode 100644 index 0000000000000..3a041cfc20c8c --- /dev/null +++ b/codersdk/gitsshkey.go @@ -0,0 +1,10 @@ +package codersdk + +import "time" + +type GitSSHKey struct { + UserID string `json:"user_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + PublicKey []byte `json:"public_key"` +} diff --git a/peerbroker/proto/peerbroker.pb.go b/peerbroker/proto/peerbroker.pb.go index b1a880bf8ce36..8a443e6e42192 100644 --- a/peerbroker/proto/peerbroker.pb.go +++ b/peerbroker/proto/peerbroker.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: peerbroker/proto/peerbroker.proto package proto diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 782bccb8c955f..aace1aa426ea6 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: provisionerd/proto/provisionerd.proto package proto diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index c5617f74cdfca..61f9beef2e055 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.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: provisionersdk/proto/provisioner.proto package proto From e3c095024ca77f5a19daecf1d1f71f73a5545905 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 1 Apr 2022 21:07:28 +0000 Subject: [PATCH 02/20] tie up cli --- cli/start.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cli/start.go b/cli/start.go index 87ac0f4d14df4..6ac6370e98c66 100644 --- a/cli/start.go +++ b/cli/start.go @@ -31,6 +31,7 @@ import ( "github.com/coder/coder/coderd" "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" + "github.com/coder/coder/coderd/gitsshkey" "github.com/coder/coder/coderd/tunnel" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/terraform" @@ -57,6 +58,7 @@ func start() *cobra.Command { useTunnel bool traceDatadog bool secureAuthCookie bool + sshKeygenAlgorithmRaw string ) root := &cobra.Command{ Use: "start", @@ -126,6 +128,12 @@ func start() *cobra.Command { if err != nil { return xerrors.Errorf("parse access url %q: %w", accessURL, err) } + + sshKeygenAlgorithm, err := gitsshkey.ParseSSHKeygenAlgorithm(sshKeygenAlgorithmRaw) + if err != nil { + return xerrors.Errorf("parse ssh keygen algorithm %s: %w", sshKeygenAlgorithmRaw, err) + } + logger := slog.Make(sloghuman.Sink(os.Stderr)) options := &coderd.Options{ AccessURL: accessURLParsed, @@ -134,6 +142,7 @@ func start() *cobra.Command { Pubsub: database.NewPubsubInMemory(), GoogleTokenValidator: validator, SecureAuthCookie: secureAuthCookie, + SSHKeygenAlgorithm: sshKeygenAlgorithm, } if !dev { @@ -337,6 +346,8 @@ func start() *cobra.Command { _ = root.Flags().MarkHidden("tunnel") cliflag.BoolVarP(root.Flags(), &traceDatadog, "trace-datadog", "", "CODER_TRACE_DATADOG", false, "Send tracing data to a datadog agent") cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false, "Specifies if the 'Secure' property is set on browser session cookies") + cliflag.StringVarP(root.Flags(), &sshKeygenAlgorithmRaw, "ssh-keygen-algorithm", "", "CODER_SSH_KEYGEN_ALGORITHM", "ed25519", "Specifies the algorithm to user for generating ssh keys. "+ + `Accepted values are "ed25519", "ecdsa", or "rsa4096"`) return root } From 7b2b5ef476bc6def09320665bfbb3a4e895051a2 Mon Sep 17 00:00:00 2001 From: Garrett Date: Fri, 1 Apr 2022 21:08:06 +0000 Subject: [PATCH 03/20] typo --- cli/start.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/start.go b/cli/start.go index 6ac6370e98c66..1d262b96a51db 100644 --- a/cli/start.go +++ b/cli/start.go @@ -346,7 +346,7 @@ func start() *cobra.Command { _ = root.Flags().MarkHidden("tunnel") cliflag.BoolVarP(root.Flags(), &traceDatadog, "trace-datadog", "", "CODER_TRACE_DATADOG", false, "Send tracing data to a datadog agent") cliflag.BoolVarP(root.Flags(), &secureAuthCookie, "secure-auth-cookie", "", "CODER_SECURE_AUTH_COOKIE", false, "Specifies if the 'Secure' property is set on browser session cookies") - cliflag.StringVarP(root.Flags(), &sshKeygenAlgorithmRaw, "ssh-keygen-algorithm", "", "CODER_SSH_KEYGEN_ALGORITHM", "ed25519", "Specifies the algorithm to user for generating ssh keys. "+ + cliflag.StringVarP(root.Flags(), &sshKeygenAlgorithmRaw, "ssh-keygen-algorithm", "", "CODER_SSH_KEYGEN_ALGORITHM", "ed25519", "Specifies the algorithm to use for generating ssh keys. "+ `Accepted values are "ed25519", "ecdsa", or "rsa4096"`) return root From 3b1145bdf9a3a68bde63281fec208ca7eccf809f Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 18:30:37 +0000 Subject: [PATCH 04/20] pr comments --- coderd/coderd.go | 4 +- coderd/coderdtest/coderdtest.go | 2 + coderd/database/dump.sql | 4 +- .../migrations/000005_gitsshkey.up.sql | 6 +-- coderd/database/models.go | 4 +- coderd/database/queries.sql.go | 8 +-- coderd/gitsshkey.go | 4 +- coderd/gitsshkey/gitsshkey.go | 50 ++++++++----------- 8 files changed, 39 insertions(+), 43 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index d0493331724b3..c04029bf9c479 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -32,7 +32,7 @@ type Options struct { GoogleTokenValidator *idtoken.Validator SecureAuthCookie bool - SSHKeygenAlgorithm gitsshkey.SSHKeygenAlgorithm + SSHKeygenAlgorithm gitsshkey.Algorithm } // New constructs the Coder API into an HTTP handler. @@ -139,7 +139,7 @@ func New(options *Options) (http.Handler, func()) { r.Get("/", api.workspacesByUser) r.Get("/{workspacename}", api.workspaceByUserAndName) }) - r.Get("/gitsshkey", api.getGitSSHKey) + r.Get("/gitsshkey", api.gitSSHKey) r.Post("/gitsshkey", api.regenerateGitSSHKey) }) }) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 88ef33639ff59..8a016325c1d8c 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -38,6 +38,7 @@ import ( "github.com/coder/coder/coderd/database" "github.com/coder/coder/coderd/database/databasefake" "github.com/coder/coder/coderd/database/postgres" + "github.com/coder/coder/coderd/gitsshkey" "github.com/coder/coder/codersdk" "github.com/coder/coder/cryptorand" "github.com/coder/coder/provisioner/echo" @@ -108,6 +109,7 @@ func New(t *testing.T, options *Options) *codersdk.Client { AWSCertificates: options.AWSInstanceIdentity, GoogleTokenValidator: options.GoogleInstanceIdentity, + SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519, }) t.Cleanup(func() { srv.Close() diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 2573d8628ab7b..6a8faf8bce407 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -93,8 +93,8 @@ CREATE TABLE git_ssh_keys ( user_id text NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, - private_key bytea NOT NULL, - public_key bytea NOT NULL + private_key text NOT NULL, + public_key text NOT NULL ); CREATE TABLE licenses ( diff --git a/coderd/database/migrations/000005_gitsshkey.up.sql b/coderd/database/migrations/000005_gitsshkey.up.sql index eaf1edd6d4f18..cfc9fb0c4c0b8 100644 --- a/coderd/database/migrations/000005_gitsshkey.up.sql +++ b/coderd/database/migrations/000005_gitsshkey.up.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS git_ssh_keys ( - user_id text PRIMARY KEY NOT NULL, + user_id uuid PRIMARY KEY NOT NULL REFERENCES users (id), created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, - private_key bytea NOT NULL, - public_key bytea NOT NULL + private_key text NOT NULL, + public_key text NOT NULL ); diff --git a/coderd/database/models.go b/coderd/database/models.go index 25fefcc69b64e..6e027bbda64e4 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -259,8 +259,8 @@ type GitSshKey struct { UserID string `db:"user_id" json:"user_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - PrivateKey []byte `db:"private_key" json:"private_key"` - PublicKey []byte `db:"public_key" json:"public_key"` + PrivateKey string `db:"private_key" json:"private_key"` + PublicKey string `db:"public_key" json:"public_key"` } type License struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index c99208c785d42..aa3778ff6845a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2716,8 +2716,8 @@ type InsertGitSSHKeyParams struct { UserID string `db:"user_id" json:"user_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - PrivateKey []byte `db:"private_key" json:"private_key"` - PublicKey []byte `db:"public_key" json:"public_key"` + PrivateKey string `db:"private_key" json:"private_key"` + PublicKey string `db:"public_key" json:"public_key"` } func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) { @@ -3576,8 +3576,8 @@ WHERE type UpdateGitSSHKeyParams struct { UserID string `db:"user_id" json:"user_id"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - PrivateKey []byte `db:"private_key" json:"private_key"` - PublicKey []byte `db:"public_key" json:"public_key"` + PrivateKey string `db:"private_key" json:"private_key"` + PublicKey string `db:"public_key" json:"public_key"` } func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error { diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 46f1547d8aa3f..cc4eb3f9461dd 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -43,7 +43,7 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { }) } -func (api *api) getGitSSHKey(rw http.ResponseWriter, r *http.Request) { +func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { var ( user = httpmw.UserParam(r) ) @@ -66,6 +66,6 @@ func (api *api) getGitSSHKey(rw http.ResponseWriter, r *http.Request) { }) } -func (api *api) getPrivateGitSSHKey(rw http.ResponseWriter, r *http.Request) { +func (api *api) privateGitSSHKey(rw http.ResponseWriter, r *http.Request) { // connect agent to workspace to user to gitsshkey } diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go index 36247e1e0064b..f4681503c5925 100644 --- a/coderd/gitsshkey/gitsshkey.go +++ b/coderd/gitsshkey/gitsshkey.go @@ -13,35 +13,33 @@ import ( "golang.org/x/xerrors" ) -type SSHKeygenAlgorithm string +type Algorithm string const ( - // SSHKeygenAlgorithmEd25519 is the Edwards-curve Digital Signature Algorithm using Curve25519 - SSHKeygenAlgorithmEd25519 SSHKeygenAlgorithm = "ed25519" - // SSHKeygenAlgorithmECDSA is the Digital Signature Algorithm (DSA) using NIST Elliptic Curve - SSHKeygenAlgorithmECDSA SSHKeygenAlgorithm = "ecdsa" - // SSHKeygenAlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm + // AlgorithmEd25519 is the Edwards-curve Digital Signature Algorithm using Curve25519 + AlgorithmEd25519 Algorithm = "ed25519" + // AlgorithmECDSA is the Digital Signature Algorithm (DSA) using NIST Elliptic Curve + AlgorithmECDSA Algorithm = "ecdsa" + // AlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm // and creates a key with a fixed size of 4096-bit. - SSHKeygenAlgorithmRSA4096 SSHKeygenAlgorithm = "rsa4096" + AlgorithmRSA4096 Algorithm = "rsa4096" ) -func GenerateKeyPair(algo SSHKeygenAlgorithm) ([]byte, []byte, error) { +func GenerateKeyPair(algo Algorithm) ([]byte, []byte, error) { switch algo { - case SSHKeygenAlgorithmEd25519: + case AlgorithmEd25519: return ed25519KeyGen() - case SSHKeygenAlgorithmECDSA: + case AlgorithmECDSA: return ecdsaKeyGen() - case SSHKeygenAlgorithmRSA4096: + case AlgorithmRSA4096: return rsa4096KeyGen() default: - return nil, nil, xerrors.Errorf("invalid SSHKeygenAlgorithm: %s", algo) + return nil, nil, xerrors.Errorf("invalid algorithm: %s", algo) } } // ed25519KeyGen returns an ED25519-based SSH private key. func ed25519KeyGen() ([]byte, []byte, error) { - const blockType = "OPENSSH PRIVATE KEY" - publicKey, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, nil, xerrors.Errorf("generate ed25519 private key: %w", err) @@ -56,7 +54,7 @@ func ed25519KeyGen() ([]byte, []byte, error) { } pb := pem.Block{ - Type: blockType, + Type: "OPENSSH PRIVATE KEY", Headers: nil, Bytes: byt, } @@ -67,15 +65,13 @@ func ed25519KeyGen() ([]byte, []byte, error) { // ecdsaKeyGen returns an ECDSA-based SSH private key. func ecdsaKeyGen() ([]byte, []byte, error) { - const blockType = "EC PRIVATE KEY" - privateKeyRaw, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, nil, xerrors.Errorf("generate ecdsa private key: %w", err) } publicKey, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) if err != nil { - return nil, nil, xerrors.Errorf("generate RSA4096 public key: %w", err) + return nil, nil, xerrors.Errorf("generate ecdsa public key: %w", err) } byt, err := x509.MarshalECPrivateKey(privateKeyRaw) @@ -84,7 +80,7 @@ func ecdsaKeyGen() ([]byte, []byte, error) { } pb := pem.Block{ - Type: blockType, + Type: "EC PRIVATE KEY", Headers: nil, Bytes: byt, } @@ -97,8 +93,6 @@ func ecdsaKeyGen() ([]byte, []byte, error) { // // Administrators may configure this for SSH key compatibility with Azure DevOps. func rsa4096KeyGen() ([]byte, []byte, error) { - const blockType = "RSA PRIVATE KEY" - privateKeyRaw, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { return nil, nil, xerrors.Errorf("generate RSA4096 private key: %w", err) @@ -109,7 +103,7 @@ func rsa4096KeyGen() ([]byte, []byte, error) { } pb := pem.Block{ - Type: blockType, + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKeyRaw), } privateKey := pem.EncodeToMemory(&pb) @@ -118,16 +112,16 @@ func rsa4096KeyGen() ([]byte, []byte, error) { } // ParseSSHKeygenAlgorithm returns a valid SSHKeygenAlgorithm or error if input is not a valid. -func ParseSSHKeygenAlgorithm(t string) (SSHKeygenAlgorithm, error) { +func ParseSSHKeygenAlgorithm(t string) (Algorithm, error) { ok := []string{ - string(SSHKeygenAlgorithmEd25519), - string(SSHKeygenAlgorithmECDSA), - string(SSHKeygenAlgorithmRSA4096), + string(AlgorithmEd25519), + string(AlgorithmECDSA), + string(AlgorithmRSA4096), } for _, a := range ok { - if string(t) == a { - return SSHKeygenAlgorithm(a), nil + if t == a { + return Algorithm(a), nil } } From 91e843554c5b687b0ab3f5389035a103d02e9144 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 18:38:25 +0000 Subject: [PATCH 05/20] fix queries --- coderd/database/dump.sql | 13 +- coderd/database/querier.go | 5 - coderd/database/queries.sql.go | 1138 -------------------------------- 3 files changed, 7 insertions(+), 1149 deletions(-) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 6a8faf8bce407..0d0c7395be69c 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -90,7 +90,7 @@ CREATE TABLE files ( ); CREATE TABLE git_ssh_keys ( - user_id text NOT NULL, + user_id uuid NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, private_key text NOT NULL, @@ -291,6 +291,9 @@ ALTER TABLE ONLY api_keys ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (hash); +ALTER TABLE ONLY git_ssh_keys + ADD CONSTRAINT git_ssh_keys_pkey PRIMARY KEY (user_id); + ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); @@ -299,11 +302,6 @@ ALTER TABLE ONLY organization_members ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); -ALTER TABLE ONLY git_ssh_keys - ADD CONSTRAINT git_ssh_keys_pkey PRIMARY KEY (user_id); - -ALTER TABLE ONLY parameter_schemas - ADD CONSTRAINT parameter_schemas_id_key UNIQUE (id); ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); @@ -392,6 +390,9 @@ CREATE UNIQUE INDEX workspaces_owner_id_name_idx ON workspaces USING btree (owne ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE ONLY git_ssh_keys + ADD CONSTRAINT git_ssh_keys_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id); + ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 8c29155bcb31f..abaed68e8049a 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -10,13 +10,10 @@ import ( type querier interface { AcquireProvisionerJob(ctx context.Context, arg AcquireProvisionerJobParams) (ProvisionerJob, error) - DeleteGitSSHKey(ctx context.Context, userID string) error DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) GetFileByHash(ctx context.Context, hash string) (File, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) - GetGitSSHKey(ctx context.Context, userID string) (GitSshKey, error) - GetOrganizationByID(ctx context.Context, id string) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) GetOrganizationsByUserID(ctx context.Context, userID uuid.UUID) ([]Organization, error) @@ -57,7 +54,6 @@ type querier interface { GetWorkspacesByUserID(ctx context.Context, arg GetWorkspacesByUserIDParams) ([]Workspace, error) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) - InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) @@ -73,7 +69,6 @@ type querier interface { InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error - UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error UpdateProjectActiveVersionByID(ctx context.Context, arg UpdateProjectActiveVersionByIDParams) error UpdateProjectDeletedByID(ctx context.Context, arg UpdateProjectDeletedByIDParams) error UpdateProjectVersionByID(ctx context.Context, arg UpdateProjectVersionByIDParams) error diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index aa3778ff6845a..49f0669143f38 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -84,23 +84,6 @@ VALUES $14, $15 ) RETURNING id, hashed_secret, user_id, application, name, last_used, expires_at, created_at, updated_at, login_type, oidc_access_token, oidc_refresh_token, oidc_id_token, oidc_expiry, devurl_token -const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec -DELETE FROM - git_ssh_keys -WHERE - user_id = $1 -` - -func (q *sqlQuerier) DeleteGitSSHKey(ctx context.Context, userID string) error { - _, err := q.db.ExecContext(ctx, deleteGitSSHKey, userID) - return err -} - -const deleteParameterValueByID = `-- name: DeleteParameterValueByID :exec -DELETE FROM - parameter_values -WHERE - id = $1 ` type InsertAPIKeyParams struct { @@ -218,7 +201,6 @@ func (q *sqlQuerier) GetFileByHash(ctx context.Context, hash string) (File, erro return i, err } -<<<<<<< HEAD:coderd/database/queries.sql.go const insertFile = `-- name: InsertFile :one INSERT INTO files (hash, created_at, created_by, mimetype, "data") @@ -249,35 +231,11 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File &i.CreatedBy, &i.Mimetype, &i.Data, -======= -const getGitSSHKey = `-- name: GetGitSSHKey :one -SELECT - user_id, created_at, updated_at, private_key, public_key -FROM - git_ssh_keys -WHERE - user_id = $1 -` - -func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID string) (GitSshKey, error) { - row := q.db.QueryRowContext(ctx, getGitSSHKey, userID) - var i GitSshKey - err := row.Scan( - &i.UserID, - &i.CreatedAt, - &i.UpdatedAt, - &i.PrivateKey, - &i.PublicKey, ->>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go ) return i, err } -<<<<<<< HEAD:coderd/database/queries.sql.go const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one -======= -const getOrganizationByID = `-- name: GetOrganizationByID :one ->>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go SELECT user_id, organization_id, created_at, updated_at, roles FROM @@ -2669,90 +2627,10 @@ VALUES type InsertWorkspaceParams struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` -<<<<<<< HEAD:coderd/database/queries.sql.go UpdatedAt time.Time `db:"updated_at" json:"updated_at"` OwnerID uuid.UUID `db:"owner_id" json:"owner_id"` ProjectID uuid.UUID `db:"project_id" json:"project_id"` Name string `db:"name" json:"name"` -======= - CreatedBy string `db:"created_by" json:"created_by"` - Mimetype string `db:"mimetype" json:"mimetype"` - Data []byte `db:"data" json:"data"` -} - -func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) { - row := q.db.QueryRowContext(ctx, insertFile, - arg.Hash, - arg.CreatedAt, - arg.CreatedBy, - arg.Mimetype, - arg.Data, - ) - var i File - err := row.Scan( - &i.Hash, - &i.CreatedAt, - &i.CreatedBy, - &i.Mimetype, - &i.Data, - ) - return i, err -} - -const insertGitSSHKey = `-- name: InsertGitSSHKey :one -INSERT INTO - git_ssh_keys ( - user_id, - created_at, - updated_at, - private_key, - public_key - ) -VALUES - ($1, $2, $3, $4, $5) RETURNING user_id, created_at, updated_at, private_key, public_key -` - -type InsertGitSSHKeyParams struct { - UserID string `db:"user_id" json:"user_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - PrivateKey string `db:"private_key" json:"private_key"` - PublicKey string `db:"public_key" json:"public_key"` -} - -func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) { - row := q.db.QueryRowContext(ctx, insertGitSSHKey, - arg.UserID, - arg.CreatedAt, - arg.UpdatedAt, - arg.PrivateKey, - arg.PublicKey, - ) - var i GitSshKey - err := row.Scan( - &i.UserID, - &i.CreatedAt, - &i.UpdatedAt, - &i.PrivateKey, - &i.PublicKey, - ) - return i, err -} - -const insertOrganization = `-- name: InsertOrganization :one -INSERT INTO - organizations (id, name, description, created_at, updated_at) -VALUES - ($1, $2, $3, $4, $5) RETURNING id, name, description, created_at, updated_at, "default", auto_off_threshold, cpu_provisioning_rate, memory_provisioning_rate, workspace_auto_off -` - -type InsertOrganizationParams struct { - ID string `db:"id" json:"id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` ->>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go } func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) { @@ -2777,1022 +2655,6 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar return i, err } -<<<<<<< HEAD:coderd/database/queries.sql.go -======= -const insertOrganizationMember = `-- name: InsertOrganizationMember :one -INSERT INTO - organization_members ( - organization_id, - user_id, - created_at, - updated_at, - roles - ) -VALUES - ($1, $2, $3, $4, $5) RETURNING organization_id, user_id, created_at, updated_at, roles -` - -type InsertOrganizationMemberParams struct { - OrganizationID string `db:"organization_id" json:"organization_id"` - UserID string `db:"user_id" json:"user_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Roles []string `db:"roles" json:"roles"` -} - -func (q *sqlQuerier) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) { - row := q.db.QueryRowContext(ctx, insertOrganizationMember, - arg.OrganizationID, - arg.UserID, - arg.CreatedAt, - arg.UpdatedAt, - pq.Array(arg.Roles), - ) - var i OrganizationMember - err := row.Scan( - &i.OrganizationID, - &i.UserID, - &i.CreatedAt, - &i.UpdatedAt, - pq.Array(&i.Roles), - ) - return i, err -} - -const insertParameterSchema = `-- name: InsertParameterSchema :one -INSERT INTO - parameter_schemas ( - id, - created_at, - job_id, - name, - description, - default_source_scheme, - default_source_value, - allow_override_source, - default_destination_scheme, - allow_override_destination, - default_refresh, - redisplay_value, - validation_error, - validation_condition, - validation_type_system, - validation_value_type - ) -VALUES - ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 - ) RETURNING id, created_at, job_id, name, description, default_source_scheme, default_source_value, allow_override_source, default_destination_scheme, allow_override_destination, default_refresh, redisplay_value, validation_error, validation_condition, validation_type_system, validation_value_type -` - -type InsertParameterSchemaParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - DefaultSourceScheme ParameterSourceScheme `db:"default_source_scheme" json:"default_source_scheme"` - DefaultSourceValue string `db:"default_source_value" json:"default_source_value"` - AllowOverrideSource bool `db:"allow_override_source" json:"allow_override_source"` - DefaultDestinationScheme ParameterDestinationScheme `db:"default_destination_scheme" json:"default_destination_scheme"` - AllowOverrideDestination bool `db:"allow_override_destination" json:"allow_override_destination"` - DefaultRefresh string `db:"default_refresh" json:"default_refresh"` - RedisplayValue bool `db:"redisplay_value" json:"redisplay_value"` - ValidationError string `db:"validation_error" json:"validation_error"` - ValidationCondition string `db:"validation_condition" json:"validation_condition"` - ValidationTypeSystem ParameterTypeSystem `db:"validation_type_system" json:"validation_type_system"` - ValidationValueType string `db:"validation_value_type" json:"validation_value_type"` -} - -func (q *sqlQuerier) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) { - row := q.db.QueryRowContext(ctx, insertParameterSchema, - arg.ID, - arg.CreatedAt, - arg.JobID, - arg.Name, - arg.Description, - arg.DefaultSourceScheme, - arg.DefaultSourceValue, - arg.AllowOverrideSource, - arg.DefaultDestinationScheme, - arg.AllowOverrideDestination, - arg.DefaultRefresh, - arg.RedisplayValue, - arg.ValidationError, - arg.ValidationCondition, - arg.ValidationTypeSystem, - arg.ValidationValueType, - ) - var i ParameterSchema - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.JobID, - &i.Name, - &i.Description, - &i.DefaultSourceScheme, - &i.DefaultSourceValue, - &i.AllowOverrideSource, - &i.DefaultDestinationScheme, - &i.AllowOverrideDestination, - &i.DefaultRefresh, - &i.RedisplayValue, - &i.ValidationError, - &i.ValidationCondition, - &i.ValidationTypeSystem, - &i.ValidationValueType, - ) - return i, err -} - -const insertParameterValue = `-- name: InsertParameterValue :one -INSERT INTO - parameter_values ( - id, - name, - created_at, - updated_at, - scope, - scope_id, - source_scheme, - source_value, - destination_scheme - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING id, created_at, updated_at, scope, scope_id, name, source_scheme, source_value, destination_scheme -` - -type InsertParameterValueParams struct { - ID uuid.UUID `db:"id" json:"id"` - Name string `db:"name" json:"name"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Scope ParameterScope `db:"scope" json:"scope"` - ScopeID string `db:"scope_id" json:"scope_id"` - SourceScheme ParameterSourceScheme `db:"source_scheme" json:"source_scheme"` - SourceValue string `db:"source_value" json:"source_value"` - DestinationScheme ParameterDestinationScheme `db:"destination_scheme" json:"destination_scheme"` -} - -func (q *sqlQuerier) InsertParameterValue(ctx context.Context, arg InsertParameterValueParams) (ParameterValue, error) { - row := q.db.QueryRowContext(ctx, insertParameterValue, - arg.ID, - arg.Name, - arg.CreatedAt, - arg.UpdatedAt, - arg.Scope, - arg.ScopeID, - arg.SourceScheme, - arg.SourceValue, - arg.DestinationScheme, - ) - var i ParameterValue - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.Scope, - &i.ScopeID, - &i.Name, - &i.SourceScheme, - &i.SourceValue, - &i.DestinationScheme, - ) - return i, err -} - -const insertProject = `-- name: InsertProject :one -INSERT INTO - projects ( - id, - created_at, - updated_at, - organization_id, - name, - provisioner, - active_version_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING id, created_at, updated_at, organization_id, deleted, name, provisioner, active_version_id -` - -type InsertProjectParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID string `db:"organization_id" json:"organization_id"` - Name string `db:"name" json:"name"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` -} - -func (q *sqlQuerier) InsertProject(ctx context.Context, arg InsertProjectParams) (Project, error) { - row := q.db.QueryRowContext(ctx, insertProject, - arg.ID, - arg.CreatedAt, - arg.UpdatedAt, - arg.OrganizationID, - arg.Name, - arg.Provisioner, - arg.ActiveVersionID, - ) - var i Project - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Deleted, - &i.Name, - &i.Provisioner, - &i.ActiveVersionID, - ) - return i, err -} - -const insertProjectVersion = `-- name: InsertProjectVersion :one -INSERT INTO - project_versions ( - id, - project_id, - organization_id, - created_at, - updated_at, - name, - description, - job_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, project_id, organization_id, created_at, updated_at, name, description, job_id -` - -type InsertProjectVersionParams struct { - ID uuid.UUID `db:"id" json:"id"` - ProjectID uuid.NullUUID `db:"project_id" json:"project_id"` - OrganizationID string `db:"organization_id" json:"organization_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` - JobID uuid.UUID `db:"job_id" json:"job_id"` -} - -func (q *sqlQuerier) InsertProjectVersion(ctx context.Context, arg InsertProjectVersionParams) (ProjectVersion, error) { - row := q.db.QueryRowContext(ctx, insertProjectVersion, - arg.ID, - arg.ProjectID, - arg.OrganizationID, - arg.CreatedAt, - arg.UpdatedAt, - arg.Name, - arg.Description, - arg.JobID, - ) - var i ProjectVersion - err := row.Scan( - &i.ID, - &i.ProjectID, - &i.OrganizationID, - &i.CreatedAt, - &i.UpdatedAt, - &i.Name, - &i.Description, - &i.JobID, - ) - return i, err -} - -const insertProvisionerDaemon = `-- name: InsertProvisionerDaemon :one -INSERT INTO - provisioner_daemons (id, created_at, organization_id, name, provisioners) -VALUES - ($1, $2, $3, $4, $5) RETURNING id, created_at, updated_at, organization_id, name, provisioners -` - -type InsertProvisionerDaemonParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - OrganizationID sql.NullString `db:"organization_id" json:"organization_id"` - Name string `db:"name" json:"name"` - Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"` -} - -func (q *sqlQuerier) InsertProvisionerDaemon(ctx context.Context, arg InsertProvisionerDaemonParams) (ProvisionerDaemon, error) { - row := q.db.QueryRowContext(ctx, insertProvisionerDaemon, - arg.ID, - arg.CreatedAt, - arg.OrganizationID, - arg.Name, - pq.Array(arg.Provisioners), - ) - var i ProvisionerDaemon - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OrganizationID, - &i.Name, - pq.Array(&i.Provisioners), - ) - return i, err -} - -const insertProvisionerJob = `-- name: InsertProvisionerJob :one -INSERT INTO - provisioner_jobs ( - id, - created_at, - updated_at, - organization_id, - initiator_id, - provisioner, - storage_method, - storage_source, - type, - input - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, started_at, canceled_at, completed_at, error, organization_id, initiator_id, provisioner, storage_method, storage_source, type, input, worker_id -` - -type InsertProvisionerJobParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OrganizationID string `db:"organization_id" json:"organization_id"` - InitiatorID string `db:"initiator_id" json:"initiator_id"` - Provisioner ProvisionerType `db:"provisioner" json:"provisioner"` - StorageMethod ProvisionerStorageMethod `db:"storage_method" json:"storage_method"` - StorageSource string `db:"storage_source" json:"storage_source"` - Type ProvisionerJobType `db:"type" json:"type"` - Input json.RawMessage `db:"input" json:"input"` -} - -func (q *sqlQuerier) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) { - row := q.db.QueryRowContext(ctx, insertProvisionerJob, - arg.ID, - arg.CreatedAt, - arg.UpdatedAt, - arg.OrganizationID, - arg.InitiatorID, - arg.Provisioner, - arg.StorageMethod, - arg.StorageSource, - arg.Type, - arg.Input, - ) - var i ProvisionerJob - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.StartedAt, - &i.CanceledAt, - &i.CompletedAt, - &i.Error, - &i.OrganizationID, - &i.InitiatorID, - &i.Provisioner, - &i.StorageMethod, - &i.StorageSource, - &i.Type, - &i.Input, - &i.WorkerID, - ) - return i, err -} - -const insertProvisionerJobLogs = `-- name: InsertProvisionerJobLogs :many -INSERT INTO - provisioner_job_logs -SELECT - unnest($1 :: uuid [ ]) AS id, - $2 :: uuid AS job_id, - unnest($3 :: timestamptz [ ]) AS created_at, - unnest($4 :: log_source [ ]) as source, - unnest($5 :: log_level [ ]) as level, - unnest($6 :: varchar(128) [ ]) as stage, - unnest($7 :: varchar(1024) [ ]) as output RETURNING id, job_id, created_at, source, level, stage, output -` - -type InsertProvisionerJobLogsParams struct { - ID []uuid.UUID `db:"id" json:"id"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` - Source []LogSource `db:"source" json:"source"` - Level []LogLevel `db:"level" json:"level"` - Stage []string `db:"stage" json:"stage"` - Output []string `db:"output" json:"output"` -} - -func (q *sqlQuerier) InsertProvisionerJobLogs(ctx context.Context, arg InsertProvisionerJobLogsParams) ([]ProvisionerJobLog, error) { - rows, err := q.db.QueryContext(ctx, insertProvisionerJobLogs, - pq.Array(arg.ID), - arg.JobID, - pq.Array(arg.CreatedAt), - pq.Array(arg.Source), - pq.Array(arg.Level), - pq.Array(arg.Stage), - pq.Array(arg.Output), - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []ProvisionerJobLog - for rows.Next() { - var i ProvisionerJobLog - if err := rows.Scan( - &i.ID, - &i.JobID, - &i.CreatedAt, - &i.Source, - &i.Level, - &i.Stage, - &i.Output, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const insertUser = `-- name: InsertUser :one -INSERT INTO - users ( - id, - email, - name, - login_type, - revoked, - hashed_password, - created_at, - updated_at, - username - ) -VALUES - ($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING id, email, name, revoked, login_type, hashed_password, created_at, updated_at, temporary_password, avatar_hash, ssh_key_regenerated_at, username, dotfiles_git_uri, roles, status, relatime, gpg_key_regenerated_at, _decomissioned, shell -` - -type InsertUserParams struct { - ID string `db:"id" json:"id"` - Email string `db:"email" json:"email"` - Name string `db:"name" json:"name"` - LoginType LoginType `db:"login_type" json:"login_type"` - HashedPassword []byte `db:"hashed_password" json:"hashed_password"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Username string `db:"username" json:"username"` -} - -func (q *sqlQuerier) InsertUser(ctx context.Context, arg InsertUserParams) (User, error) { - row := q.db.QueryRowContext(ctx, insertUser, - arg.ID, - arg.Email, - arg.Name, - arg.LoginType, - arg.HashedPassword, - arg.CreatedAt, - arg.UpdatedAt, - arg.Username, - ) - var i User - err := row.Scan( - &i.ID, - &i.Email, - &i.Name, - &i.Revoked, - &i.LoginType, - &i.HashedPassword, - &i.CreatedAt, - &i.UpdatedAt, - &i.TemporaryPassword, - &i.AvatarHash, - &i.SshKeyRegeneratedAt, - &i.Username, - &i.DotfilesGitUri, - pq.Array(&i.Roles), - &i.Status, - &i.Relatime, - &i.GpgKeyRegeneratedAt, - &i.Decomissioned, - &i.Shell, - ) - return i, err -} - -const insertWorkspace = `-- name: InsertWorkspace :one -INSERT INTO - workspaces ( - id, - created_at, - updated_at, - owner_id, - project_id, - name - ) -VALUES - ($1, $2, $3, $4, $5, $6) RETURNING id, created_at, updated_at, owner_id, project_id, deleted, name -` - -type InsertWorkspaceParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - OwnerID string `db:"owner_id" json:"owner_id"` - ProjectID uuid.UUID `db:"project_id" json:"project_id"` - Name string `db:"name" json:"name"` -} - -func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) { - row := q.db.QueryRowContext(ctx, insertWorkspace, - arg.ID, - arg.CreatedAt, - arg.UpdatedAt, - arg.OwnerID, - arg.ProjectID, - arg.Name, - ) - var i Workspace - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.OwnerID, - &i.ProjectID, - &i.Deleted, - &i.Name, - ) - return i, err -} - -const insertWorkspaceAgent = `-- name: InsertWorkspaceAgent :one -INSERT INTO - workspace_agents ( - id, - created_at, - updated_at, - resource_id, - auth_token, - auth_instance_id, - environment_variables, - startup_script, - instance_metadata, - resource_metadata - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id, created_at, updated_at, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, environment_variables, startup_script, instance_metadata, resource_metadata -` - -type InsertWorkspaceAgentParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` - AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` - AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` - EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` - StartupScript sql.NullString `db:"startup_script" json:"startup_script"` - InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` - ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` -} - -func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) { - row := q.db.QueryRowContext(ctx, insertWorkspaceAgent, - arg.ID, - arg.CreatedAt, - arg.UpdatedAt, - arg.ResourceID, - arg.AuthToken, - arg.AuthInstanceID, - arg.EnvironmentVariables, - arg.StartupScript, - arg.InstanceMetadata, - arg.ResourceMetadata, - ) - var i WorkspaceAgent - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.FirstConnectedAt, - &i.LastConnectedAt, - &i.DisconnectedAt, - &i.ResourceID, - &i.AuthToken, - &i.AuthInstanceID, - &i.EnvironmentVariables, - &i.StartupScript, - &i.InstanceMetadata, - &i.ResourceMetadata, - ) - return i, err -} - -const insertWorkspaceBuild = `-- name: InsertWorkspaceBuild :one -INSERT INTO - workspace_builds ( - id, - created_at, - updated_at, - workspace_id, - project_version_id, - before_id, - name, - transition, - initiator, - job_id, - provisioner_state - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING id, created_at, updated_at, workspace_id, project_version_id, name, before_id, after_id, transition, initiator, provisioner_state, job_id -` - -type InsertWorkspaceBuildParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"` - ProjectVersionID uuid.UUID `db:"project_version_id" json:"project_version_id"` - BeforeID uuid.NullUUID `db:"before_id" json:"before_id"` - Name string `db:"name" json:"name"` - Transition WorkspaceTransition `db:"transition" json:"transition"` - Initiator string `db:"initiator" json:"initiator"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` -} - -func (q *sqlQuerier) InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) { - row := q.db.QueryRowContext(ctx, insertWorkspaceBuild, - arg.ID, - arg.CreatedAt, - arg.UpdatedAt, - arg.WorkspaceID, - arg.ProjectVersionID, - arg.BeforeID, - arg.Name, - arg.Transition, - arg.Initiator, - arg.JobID, - arg.ProvisionerState, - ) - var i WorkspaceBuild - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.UpdatedAt, - &i.WorkspaceID, - &i.ProjectVersionID, - &i.Name, - &i.BeforeID, - &i.AfterID, - &i.Transition, - &i.Initiator, - &i.ProvisionerState, - &i.JobID, - ) - return i, err -} - -const insertWorkspaceResource = `-- name: InsertWorkspaceResource :one -INSERT INTO - workspace_resources ( - id, - created_at, - job_id, - transition, - address, - type, - name, - agent_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id, created_at, job_id, transition, address, type, name, agent_id -` - -type InsertWorkspaceResourceParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - JobID uuid.UUID `db:"job_id" json:"job_id"` - Transition WorkspaceTransition `db:"transition" json:"transition"` - Address string `db:"address" json:"address"` - Type string `db:"type" json:"type"` - Name string `db:"name" json:"name"` - AgentID uuid.NullUUID `db:"agent_id" json:"agent_id"` -} - -func (q *sqlQuerier) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) { - row := q.db.QueryRowContext(ctx, insertWorkspaceResource, - arg.ID, - arg.CreatedAt, - arg.JobID, - arg.Transition, - arg.Address, - arg.Type, - arg.Name, - arg.AgentID, - ) - var i WorkspaceResource - err := row.Scan( - &i.ID, - &i.CreatedAt, - &i.JobID, - &i.Transition, - &i.Address, - &i.Type, - &i.Name, - &i.AgentID, - ) - return i, err -} - -const updateAPIKeyByID = `-- name: UpdateAPIKeyByID :exec -UPDATE - api_keys -SET - last_used = $2, - expires_at = $3, - oidc_access_token = $4, - oidc_refresh_token = $5, - oidc_expiry = $6 -WHERE - id = $1 -` - -type UpdateAPIKeyByIDParams struct { - ID string `db:"id" json:"id"` - LastUsed time.Time `db:"last_used" json:"last_used"` - ExpiresAt time.Time `db:"expires_at" json:"expires_at"` - OIDCAccessToken string `db:"oidc_access_token" json:"oidc_access_token"` - OIDCRefreshToken string `db:"oidc_refresh_token" json:"oidc_refresh_token"` - OIDCExpiry time.Time `db:"oidc_expiry" json:"oidc_expiry"` -} - -func (q *sqlQuerier) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error { - _, err := q.db.ExecContext(ctx, updateAPIKeyByID, - arg.ID, - arg.LastUsed, - arg.ExpiresAt, - arg.OIDCAccessToken, - arg.OIDCRefreshToken, - arg.OIDCExpiry, - ) - return err -} - -const updateGitSSHKey = `-- name: UpdateGitSSHKey :exec -UPDATE - git_ssh_keys -SET - updated_at = $2, - private_key = $3, - public_key = $4 -WHERE - user_id = $1 -` - -type UpdateGitSSHKeyParams struct { - UserID string `db:"user_id" json:"user_id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - PrivateKey string `db:"private_key" json:"private_key"` - PublicKey string `db:"public_key" json:"public_key"` -} - -func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error { - _, err := q.db.ExecContext(ctx, updateGitSSHKey, - arg.UserID, - arg.UpdatedAt, - arg.PrivateKey, - arg.PublicKey, - ) - return err -} - -const updateProjectActiveVersionByID = `-- name: UpdateProjectActiveVersionByID :exec -UPDATE - projects -SET - active_version_id = $2 -WHERE - id = $1 -` - -type UpdateProjectActiveVersionByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - ActiveVersionID uuid.UUID `db:"active_version_id" json:"active_version_id"` -} - -func (q *sqlQuerier) UpdateProjectActiveVersionByID(ctx context.Context, arg UpdateProjectActiveVersionByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProjectActiveVersionByID, arg.ID, arg.ActiveVersionID) - return err -} - -const updateProjectDeletedByID = `-- name: UpdateProjectDeletedByID :exec -UPDATE - projects -SET - deleted = $2 -WHERE - id = $1 -` - -type UpdateProjectDeletedByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - Deleted bool `db:"deleted" json:"deleted"` -} - -func (q *sqlQuerier) UpdateProjectDeletedByID(ctx context.Context, arg UpdateProjectDeletedByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProjectDeletedByID, arg.ID, arg.Deleted) - return err -} - -const updateProjectVersionByID = `-- name: UpdateProjectVersionByID :exec -UPDATE - project_versions -SET - project_id = $2, - updated_at = $3 -WHERE - id = $1 -` - -type UpdateProjectVersionByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - ProjectID uuid.NullUUID `db:"project_id" json:"project_id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` -} - -func (q *sqlQuerier) UpdateProjectVersionByID(ctx context.Context, arg UpdateProjectVersionByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProjectVersionByID, arg.ID, arg.ProjectID, arg.UpdatedAt) - return err -} - -const updateProvisionerDaemonByID = `-- name: UpdateProvisionerDaemonByID :exec -UPDATE - provisioner_daemons -SET - updated_at = $2, - provisioners = $3 -WHERE - id = $1 -` - -type UpdateProvisionerDaemonByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - UpdatedAt sql.NullTime `db:"updated_at" json:"updated_at"` - Provisioners []ProvisionerType `db:"provisioners" json:"provisioners"` -} - -func (q *sqlQuerier) UpdateProvisionerDaemonByID(ctx context.Context, arg UpdateProvisionerDaemonByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProvisionerDaemonByID, arg.ID, arg.UpdatedAt, pq.Array(arg.Provisioners)) - return err -} - -const updateProvisionerJobByID = `-- name: UpdateProvisionerJobByID :exec -UPDATE - provisioner_jobs -SET - updated_at = $2 -WHERE - id = $1 -` - -type UpdateProvisionerJobByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` -} - -func (q *sqlQuerier) UpdateProvisionerJobByID(ctx context.Context, arg UpdateProvisionerJobByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProvisionerJobByID, arg.ID, arg.UpdatedAt) - return err -} - -const updateProvisionerJobWithCancelByID = `-- name: UpdateProvisionerJobWithCancelByID :exec -UPDATE - provisioner_jobs -SET - canceled_at = $2 -WHERE - id = $1 -` - -type UpdateProvisionerJobWithCancelByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - CanceledAt sql.NullTime `db:"canceled_at" json:"canceled_at"` -} - -func (q *sqlQuerier) UpdateProvisionerJobWithCancelByID(ctx context.Context, arg UpdateProvisionerJobWithCancelByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProvisionerJobWithCancelByID, arg.ID, arg.CanceledAt) - return err -} - -const updateProvisionerJobWithCompleteByID = `-- name: UpdateProvisionerJobWithCompleteByID :exec -UPDATE - provisioner_jobs -SET - updated_at = $2, - completed_at = $3, - error = $4 -WHERE - id = $1 -` - -type UpdateProvisionerJobWithCompleteByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - CompletedAt sql.NullTime `db:"completed_at" json:"completed_at"` - Error sql.NullString `db:"error" json:"error"` -} - -func (q *sqlQuerier) UpdateProvisionerJobWithCompleteByID(ctx context.Context, arg UpdateProvisionerJobWithCompleteByIDParams) error { - _, err := q.db.ExecContext(ctx, updateProvisionerJobWithCompleteByID, - arg.ID, - arg.UpdatedAt, - arg.CompletedAt, - arg.Error, - ) - return err -} - -const updateWorkspaceAgentConnectionByID = `-- name: UpdateWorkspaceAgentConnectionByID :exec -UPDATE - workspace_agents -SET - first_connected_at = $2, - last_connected_at = $3, - disconnected_at = $4 -WHERE - id = $1 -` - -type UpdateWorkspaceAgentConnectionByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - FirstConnectedAt sql.NullTime `db:"first_connected_at" json:"first_connected_at"` - LastConnectedAt sql.NullTime `db:"last_connected_at" json:"last_connected_at"` - DisconnectedAt sql.NullTime `db:"disconnected_at" json:"disconnected_at"` -} - -func (q *sqlQuerier) UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error { - _, err := q.db.ExecContext(ctx, updateWorkspaceAgentConnectionByID, - arg.ID, - arg.FirstConnectedAt, - arg.LastConnectedAt, - arg.DisconnectedAt, - ) - return err -} - -const updateWorkspaceBuildByID = `-- name: UpdateWorkspaceBuildByID :exec -UPDATE - workspace_builds -SET - updated_at = $2, - after_id = $3, - provisioner_state = $4 -WHERE - id = $1 -` - -type UpdateWorkspaceBuildByIDParams struct { - ID uuid.UUID `db:"id" json:"id"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - AfterID uuid.NullUUID `db:"after_id" json:"after_id"` - ProvisionerState []byte `db:"provisioner_state" json:"provisioner_state"` -} - -func (q *sqlQuerier) UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) error { - _, err := q.db.ExecContext(ctx, updateWorkspaceBuildByID, - arg.ID, - arg.UpdatedAt, - arg.AfterID, - arg.ProvisionerState, - ) - return err -} - ->>>>>>> feat: Add user scoped git ssh keys:coderd/database/query.sql.go const updateWorkspaceDeletedByID = `-- name: UpdateWorkspaceDeletedByID :exec UPDATE workspaces From 56c64370442a015549d1678cc7358c9b1240b842 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 18:58:33 +0000 Subject: [PATCH 06/20] add gitsshkeys queries --- coderd/database/queries/gitsshkeys.sql | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 coderd/database/queries/gitsshkeys.sql diff --git a/coderd/database/queries/gitsshkeys.sql b/coderd/database/queries/gitsshkeys.sql new file mode 100644 index 0000000000000..4bc6dd5afcf5b --- /dev/null +++ b/coderd/database/queries/gitsshkeys.sql @@ -0,0 +1,35 @@ +-- name: InsertGitSSHKey :one +INSERT INTO + git_ssh_keys ( + user_id, + created_at, + updated_at, + private_key, + public_key + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING *; + +-- name: GetGitSSHKey :one +SELECT + * +FROM + git_ssh_keys +WHERE + user_id = $1; + +-- name: UpdateGitSSHKey :exec +UPDATE + git_ssh_keys +SET + updated_at = $2, + private_key = $3, + public_key = $4 +WHERE + user_id = $1; + +-- name: DeleteGitSSHKey :exec +DELETE FROM + git_ssh_keys +WHERE + user_id = $1; From 8e6ca2f8c3eb6444837e558cd91a33e445205c2d Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 19:05:20 +0000 Subject: [PATCH 07/20] rm --- coderd/database/query.sql | 827 -------------------------------------- 1 file changed, 827 deletions(-) delete mode 100644 coderd/database/query.sql diff --git a/coderd/database/query.sql b/coderd/database/query.sql deleted file mode 100644 index cbd5a5ac9bb21..0000000000000 --- a/coderd/database/query.sql +++ /dev/null @@ -1,827 +0,0 @@ --- Database queries are generated using sqlc. See: --- https://docs.sqlc.dev/en/latest/tutorials/getting-started-postgresql.html --- --- Run "make gen" to generate models and query functions. -; - --- Acquires the lock for a single job that isn't started, completed, --- canceled, and that matches an array of provisioner types. --- --- SKIP LOCKED is used to jump over locked rows. This prevents --- multiple provisioners from acquiring the same jobs. See: --- https://www.postgresql.org/docs/9.5/sql-select.html#SQL-FOR-UPDATE-SHARE --- name: AcquireProvisionerJob :one -UPDATE - provisioner_jobs -SET - started_at = @started_at, - updated_at = @started_at, - worker_id = @worker_id -WHERE - id = ( - SELECT - id - FROM - provisioner_jobs AS nested - WHERE - nested.started_at IS NULL - AND nested.canceled_at IS NULL - AND nested.completed_at IS NULL - AND nested.provisioner = ANY(@types :: provisioner_type [ ]) - ORDER BY - nested.created_at FOR - UPDATE - SKIP LOCKED - LIMIT - 1 - ) RETURNING *; - --- name: DeleteParameterValueByID :exec -DELETE FROM - parameter_values -WHERE - id = $1; - --- name: GetAPIKeyByID :one -SELECT - * -FROM - api_keys -WHERE - id = $1 -LIMIT - 1; - --- name: GetFileByHash :one -SELECT - * -FROM - files -WHERE - hash = $1 -LIMIT - 1; - --- name: GetUserByID :one -SELECT - * -FROM - users -WHERE - id = $1 -LIMIT - 1; - --- name: GetUserByEmailOrUsername :one -SELECT - * -FROM - users -WHERE - LOWER(username) = LOWER(@username) - OR email = @email -LIMIT - 1; - --- name: GetUserCount :one -SELECT - COUNT(*) -FROM - users; - --- name: GetOrganizationByID :one -SELECT - * -FROM - organizations -WHERE - id = $1; - --- name: GetOrganizationByName :one -SELECT - * -FROM - organizations -WHERE - LOWER(name) = LOWER(@name) -LIMIT - 1; - --- name: GetOrganizationsByUserID :many -SELECT - * -FROM - organizations -WHERE - id = ( - SELECT - organization_id - FROM - organization_members - WHERE - user_id = $1 - ); - --- name: GetOrganizationMemberByUserID :one -SELECT - * -FROM - organization_members -WHERE - organization_id = $1 - AND user_id = $2 -LIMIT - 1; - --- name: GetParameterValuesByScope :many -SELECT - * -FROM - parameter_values -WHERE - scope = $1 - AND scope_id = $2; - --- name: GetParameterValueByScopeAndName :one -SELECT - * -FROM - parameter_values -WHERE - scope = $1 - AND scope_id = $2 - AND name = $3 -LIMIT - 1; - --- name: GetProjectByID :one -SELECT - * -FROM - projects -WHERE - id = $1 -LIMIT - 1; - --- name: GetProjectsByIDs :many -SELECT - * -FROM - projects -WHERE - id = ANY(@ids :: uuid [ ]); - --- name: GetProjectByOrganizationAndName :one -SELECT - * -FROM - projects -WHERE - organization_id = @organization_id - AND deleted = @deleted - AND LOWER(name) = LOWER(@name) -LIMIT - 1; - --- name: GetProjectsByOrganization :many -SELECT - * -FROM - projects -WHERE - organization_id = $1 - AND deleted = $2; - --- name: GetParameterSchemasByJobID :many -SELECT - * -FROM - parameter_schemas -WHERE - job_id = $1; - --- name: GetProjectVersionsByProjectID :many -SELECT - * -FROM - project_versions -WHERE - project_id = $1 :: uuid; - --- name: GetProjectVersionByJobID :one -SELECT - * -FROM - project_versions -WHERE - job_id = $1; - --- name: GetProjectVersionByProjectIDAndName :one -SELECT - * -FROM - project_versions -WHERE - project_id = $1 - AND name = $2; - --- name: GetProjectVersionByID :one -SELECT - * -FROM - project_versions -WHERE - id = $1; - --- name: GetProvisionerLogsByIDBetween :many -SELECT - * -FROM - provisioner_job_logs -WHERE - job_id = @job_id - AND ( - created_at >= @created_after - OR created_at <= @created_before - ) -ORDER BY - created_at; - --- name: GetProvisionerDaemonByID :one -SELECT - * -FROM - provisioner_daemons -WHERE - id = $1; - --- name: GetProvisionerDaemons :many -SELECT - * -FROM - provisioner_daemons; - --- name: GetWorkspaceAgentByAuthToken :one -SELECT - * -FROM - workspace_agents -WHERE - auth_token = $1 -ORDER BY - created_at DESC; - --- name: GetWorkspaceAgentByInstanceID :one -SELECT - * -FROM - workspace_agents -WHERE - auth_instance_id = @auth_instance_id :: text -ORDER BY - created_at DESC; - --- name: GetProvisionerJobByID :one -SELECT - * -FROM - provisioner_jobs -WHERE - id = $1; - --- name: GetProvisionerJobsByIDs :many -SELECT - * -FROM - provisioner_jobs -WHERE - id = ANY(@ids :: uuid [ ]); - --- name: GetWorkspaceByID :one -SELECT - * -FROM - workspaces -WHERE - id = $1 -LIMIT - 1; - --- name: GetWorkspacesByProjectID :many -SELECT - * -FROM - workspaces -WHERE - project_id = $1 - AND deleted = $2; - --- name: GetWorkspacesByUserID :many -SELECT - * -FROM - workspaces -WHERE - owner_id = $1 - AND deleted = $2; - --- name: GetWorkspaceByUserIDAndName :one -SELECT - * -FROM - workspaces -WHERE - owner_id = @owner_id - AND deleted = @deleted - AND LOWER(name) = LOWER(@name); - --- name: GetWorkspaceOwnerCountsByProjectIDs :many -SELECT - project_id, - COUNT(DISTINCT owner_id) -FROM - workspaces -WHERE - project_id = ANY(@ids :: uuid [ ]) -GROUP BY - project_id, - owner_id; - --- name: GetWorkspaceBuildByID :one -SELECT - * -FROM - workspace_builds -WHERE - id = $1 -LIMIT - 1; - --- name: GetWorkspaceBuildByJobID :one -SELECT - * -FROM - workspace_builds -WHERE - job_id = $1 -LIMIT - 1; - --- name: GetWorkspaceBuildByWorkspaceIDAndName :one -SELECT - * -FROM - workspace_builds -WHERE - workspace_id = $1 - AND name = $2; - --- name: GetWorkspaceBuildByWorkspaceID :many -SELECT - * -FROM - workspace_builds -WHERE - workspace_id = $1; - --- name: GetWorkspaceBuildByWorkspaceIDWithoutAfter :one -SELECT - * -FROM - workspace_builds -WHERE - workspace_id = $1 - AND after_id IS NULL -LIMIT - 1; - --- name: GetWorkspaceBuildsByWorkspaceIDsWithoutAfter :many -SELECT - * -FROM - workspace_builds -WHERE - workspace_id = ANY(@ids :: uuid [ ]) - AND after_id IS NULL; - --- name: GetWorkspaceResourceByID :one -SELECT - * -FROM - workspace_resources -WHERE - id = $1; - --- name: GetWorkspaceResourcesByJobID :many -SELECT - * -FROM - workspace_resources -WHERE - job_id = $1; - --- name: GetWorkspaceAgentByResourceID :one -SELECT - * -FROM - workspace_agents -WHERE - resource_id = $1; - --- name: InsertAPIKey :one -INSERT INTO - api_keys ( - id, - hashed_secret, - user_id, - application, - name, - last_used, - expires_at, - created_at, - updated_at, - login_type, - oidc_access_token, - oidc_refresh_token, - oidc_id_token, - oidc_expiry, - devurl_token - ) -VALUES - ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15 - ) RETURNING *; - --- name: InsertFile :one -INSERT INTO - files (hash, created_at, created_by, mimetype, data) -VALUES - ($1, $2, $3, $4, $5) RETURNING *; - --- name: InsertProvisionerJobLogs :many -INSERT INTO - provisioner_job_logs -SELECT - unnest(@id :: uuid [ ]) AS id, - @job_id :: uuid AS job_id, - unnest(@created_at :: timestamptz [ ]) AS created_at, - unnest(@source :: log_source [ ]) as source, - unnest(@level :: log_level [ ]) as level, - unnest(@stage :: varchar(128) [ ]) as stage, - unnest(@output :: varchar(1024) [ ]) as output RETURNING *; - --- name: InsertOrganization :one -INSERT INTO - organizations (id, name, description, created_at, updated_at) -VALUES - ($1, $2, $3, $4, $5) RETURNING *; - --- name: InsertOrganizationMember :one -INSERT INTO - organization_members ( - organization_id, - user_id, - created_at, - updated_at, - roles - ) -VALUES - ($1, $2, $3, $4, $5) RETURNING *; - --- name: InsertParameterValue :one -INSERT INTO - parameter_values ( - id, - name, - created_at, - updated_at, - scope, - scope_id, - source_scheme, - source_value, - destination_scheme - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *; - --- name: InsertProject :one -INSERT INTO - projects ( - id, - created_at, - updated_at, - organization_id, - name, - provisioner, - active_version_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7) RETURNING *; - --- name: InsertWorkspaceResource :one -INSERT INTO - workspace_resources ( - id, - created_at, - job_id, - transition, - address, - type, - name, - agent_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; - --- name: InsertProjectVersion :one -INSERT INTO - project_versions ( - id, - project_id, - organization_id, - created_at, - updated_at, - name, - description, - job_id - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *; - --- name: InsertParameterSchema :one -INSERT INTO - parameter_schemas ( - id, - created_at, - job_id, - name, - description, - default_source_scheme, - default_source_value, - allow_override_source, - default_destination_scheme, - allow_override_destination, - default_refresh, - redisplay_value, - validation_error, - validation_condition, - validation_type_system, - validation_value_type - ) -VALUES - ( - $1, - $2, - $3, - $4, - $5, - $6, - $7, - $8, - $9, - $10, - $11, - $12, - $13, - $14, - $15, - $16 - ) RETURNING *; - --- name: InsertProvisionerDaemon :one -INSERT INTO - provisioner_daemons (id, created_at, organization_id, name, provisioners) -VALUES - ($1, $2, $3, $4, $5) RETURNING *; - --- name: InsertProvisionerJob :one -INSERT INTO - provisioner_jobs ( - id, - created_at, - updated_at, - organization_id, - initiator_id, - provisioner, - storage_method, - storage_source, - type, - input - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; - --- name: InsertUser :one -INSERT INTO - users ( - id, - email, - name, - login_type, - revoked, - hashed_password, - created_at, - updated_at, - username - ) -VALUES - ($1, $2, $3, $4, false, $5, $6, $7, $8) RETURNING *; - --- name: InsertWorkspace :one -INSERT INTO - workspaces ( - id, - created_at, - updated_at, - owner_id, - project_id, - name - ) -VALUES - ($1, $2, $3, $4, $5, $6) RETURNING *; - --- name: InsertWorkspaceAgent :one -INSERT INTO - workspace_agents ( - id, - created_at, - updated_at, - resource_id, - auth_token, - auth_instance_id, - environment_variables, - startup_script, - instance_metadata, - resource_metadata - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *; - --- name: InsertWorkspaceBuild :one -INSERT INTO - workspace_builds ( - id, - created_at, - updated_at, - workspace_id, - project_version_id, - before_id, - name, - transition, - initiator, - job_id, - provisioner_state - ) -VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *; - --- name: UpdateAPIKeyByID :exec -UPDATE - api_keys -SET - last_used = $2, - expires_at = $3, - oidc_access_token = $4, - oidc_refresh_token = $5, - oidc_expiry = $6 -WHERE - id = $1; - --- name: UpdateProjectActiveVersionByID :exec -UPDATE - projects -SET - active_version_id = $2 -WHERE - id = $1; - --- name: UpdateProjectDeletedByID :exec -UPDATE - projects -SET - deleted = $2 -WHERE - id = $1; - --- name: UpdateProjectVersionByID :exec -UPDATE - project_versions -SET - project_id = $2, - updated_at = $3 -WHERE - id = $1; - --- name: UpdateProvisionerDaemonByID :exec -UPDATE - provisioner_daemons -SET - updated_at = $2, - provisioners = $3 -WHERE - id = $1; - --- name: UpdateProvisionerJobByID :exec -UPDATE - provisioner_jobs -SET - updated_at = $2 -WHERE - id = $1; - --- name: UpdateProvisionerJobWithCancelByID :exec -UPDATE - provisioner_jobs -SET - canceled_at = $2 -WHERE - id = $1; - --- name: UpdateProvisionerJobWithCompleteByID :exec -UPDATE - provisioner_jobs -SET - updated_at = $2, - completed_at = $3, - error = $4 -WHERE - id = $1; - --- name: UpdateWorkspaceDeletedByID :exec -UPDATE - workspaces -SET - deleted = $2 -WHERE - id = $1; - --- name: UpdateWorkspaceAgentConnectionByID :exec -UPDATE - workspace_agents -SET - first_connected_at = $2, - last_connected_at = $3, - disconnected_at = $4 -WHERE - id = $1; - --- name: UpdateWorkspaceBuildByID :exec -UPDATE - workspace_builds -SET - updated_at = $2, - after_id = $3, - provisioner_state = $4 -WHERE - id = $1; - --- name: InsertGitSSHKey :one -INSERT INTO - git_ssh_keys ( - user_id, - created_at, - updated_at, - private_key, - public_key - ) -VALUES - ($1, $2, $3, $4, $5) RETURNING *; - --- name: GetGitSSHKey :one -SELECT - * -FROM - git_ssh_keys -WHERE - user_id = $1; - --- name: UpdateGitSSHKey :exec -UPDATE - git_ssh_keys -SET - updated_at = $2, - private_key = $3, - public_key = $4 -WHERE - user_id = $1; - --- name: DeleteGitSSHKey :exec -DELETE FROM - git_ssh_keys -WHERE - user_id = $1; From 214bb417bff29d3f35cc1efee77e32a0aed15265 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 19:06:44 +0000 Subject: [PATCH 08/20] make gen --- coderd/database/queries.sql.go | 102 +++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 49f0669143f38..6e5b4f8a5782b 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -235,6 +235,108 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File return i, err } +const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec +DELETE FROM + git_ssh_keys +WHERE + user_id = $1 +` + +func (q *sqlQuerier) DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error { + _, err := q.db.ExecContext(ctx, deleteGitSSHKey, userID) + return err +} + +const getGitSSHKey = `-- name: GetGitSSHKey :one +SELECT + user_id, created_at, updated_at, private_key, public_key +FROM + git_ssh_keys +WHERE + user_id = $1 +` + +func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSshKey, error) { + row := q.db.QueryRowContext(ctx, getGitSSHKey, userID) + var i GitSshKey + err := row.Scan( + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + &i.PrivateKey, + &i.PublicKey, + ) + return i, err +} + +const insertGitSSHKey = `-- name: InsertGitSSHKey :one +INSERT INTO + git_ssh_keys ( + user_id, + created_at, + updated_at, + private_key, + public_key + ) +VALUES + ($1, $2, $3, $4, $5) RETURNING user_id, created_at, updated_at, private_key, public_key +` + +type InsertGitSSHKeyParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + PrivateKey string `db:"private_key" json:"private_key"` + PublicKey string `db:"public_key" json:"public_key"` +} + +func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) { + row := q.db.QueryRowContext(ctx, insertGitSSHKey, + arg.UserID, + arg.CreatedAt, + arg.UpdatedAt, + arg.PrivateKey, + arg.PublicKey, + ) + var i GitSshKey + err := row.Scan( + &i.UserID, + &i.CreatedAt, + &i.UpdatedAt, + &i.PrivateKey, + &i.PublicKey, + ) + return i, err +} + +const updateGitSSHKey = `-- name: UpdateGitSSHKey :exec +UPDATE + git_ssh_keys +SET + updated_at = $2, + private_key = $3, + public_key = $4 +WHERE + user_id = $1 +` + +type UpdateGitSSHKeyParams struct { + UserID uuid.UUID `db:"user_id" json:"user_id"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + PrivateKey string `db:"private_key" json:"private_key"` + PublicKey string `db:"public_key" json:"public_key"` +} + +func (q *sqlQuerier) UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error { + _, err := q.db.ExecContext(ctx, updateGitSSHKey, + arg.UserID, + arg.UpdatedAt, + arg.PrivateKey, + arg.PublicKey, + ) + return err +} + const getOrganizationMemberByUserID = `-- name: GetOrganizationMemberByUserID :one SELECT user_id, organization_id, created_at, updated_at, roles From 66ce653c51d62fb49244df1a7c895e68168bc112 Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 19:25:24 +0000 Subject: [PATCH 09/20] make gen 2 --- coderd/database/querier.go | 4 ++++ coderd/gitsshkey.go | 4 ++-- codersdk/gitsshkey.go | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/coderd/database/querier.go b/coderd/database/querier.go index abaed68e8049a..b761c42b15b46 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -10,9 +10,11 @@ import ( type querier interface { AcquireProvisionerJob(ctx context.Context, arg AcquireProvisionerJobParams) (ProvisionerJob, error) + DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) GetFileByHash(ctx context.Context, hash string) (File, error) + GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSshKey, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) @@ -54,6 +56,7 @@ type querier interface { GetWorkspacesByUserID(ctx context.Context, arg GetWorkspacesByUserIDParams) ([]Workspace, error) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) + InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) @@ -69,6 +72,7 @@ type querier interface { InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) (WorkspaceBuild, error) InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error) UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error + UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) error UpdateProjectActiveVersionByID(ctx context.Context, arg UpdateProjectActiveVersionByIDParams) error UpdateProjectDeletedByID(ctx context.Context, arg UpdateProjectDeletedByIDParams) error UpdateProjectVersionByID(ctx context.Context, arg UpdateProjectVersionByIDParams) error diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index cc4eb3f9461dd..1625c64c16a85 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -28,8 +28,8 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { err = api.Database.UpdateGitSSHKey(r.Context(), database.UpdateGitSSHKeyParams{ UserID: user.ID, UpdatedAt: database.Now(), - PrivateKey: privateKey, - PublicKey: publicKey, + PrivateKey: string(privateKey), + PublicKey: string(publicKey), }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 3a041cfc20c8c..9b9aff2c57f68 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -6,5 +6,5 @@ type GitSSHKey struct { UserID string `json:"user_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - PublicKey []byte `json:"public_key"` + PublicKey string `json:"public_key"` } From 6a10a3c616678a6cc75ee5277b0c46c781f3a0ef Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 20:43:41 +0000 Subject: [PATCH 10/20] add agent route --- coderd/database/databasefake/databasefake.go | 62 ++++++++++++++++++++ coderd/database/models.go | 2 +- coderd/gitsshkey.go | 50 +++++++++++++++- coderd/gitsshkey/gitsshkey.go | 42 +++++++------ codersdk/gitsshkey.go | 16 ++++- 5 files changed, 147 insertions(+), 25 deletions(-) diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index f338a05bd8ee2..a1198c9522fc8 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -31,6 +31,7 @@ func New() database.Store { provisionerJobResource: make([]database.WorkspaceResource, 0), workspaceBuild: make([]database.WorkspaceBuild, 0), provisionerJobAgent: make([]database.WorkspaceAgent, 0), + gitsshkey: make([]database.GitSshKey, 0), } } @@ -57,6 +58,7 @@ type fakeQuerier struct { provisionerJobLog []database.ProvisionerJobLog workspace []database.Workspace workspaceBuild []database.WorkspaceBuild + gitsshkey []database.GitSshKey } // InTx doesn't rollback data properly for in-memory yet. @@ -1239,3 +1241,63 @@ func (q *fakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database } return sql.ErrNoRows } + +func (q *fakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitSSHKeyParams) (database.GitSshKey, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + //nolint:gosimple + gitsshkey := database.GitSshKey{ + UserID: arg.UserID, + CreatedAt: arg.CreatedAt, + UpdatedAt: arg.UpdatedAt, + PrivateKey: arg.PrivateKey, + PublicKey: arg.PublicKey, + } + q.gitsshkey = append(q.gitsshkey, gitsshkey) + return gitsshkey, nil +} + +func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSshKey, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + for _, key := range q.gitsshkey { + if key.UserID == userID { + return key, nil + } + } + return database.GitSshKey{}, sql.ErrNoRows +} + +func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) error { + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, key := range q.gitsshkey { + if key.UserID.String() != arg.UserID.String() { + continue + } + key.UpdatedAt = arg.UpdatedAt + key.PrivateKey = arg.PrivateKey + key.PublicKey = arg.PublicKey + q.gitsshkey[index] = key + return nil + } + return sql.ErrNoRows +} + +func (q *fakeQuerier) DeleteGitSSHKey(_ context.Context, userID uuid.UUID) error { + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, key := range q.gitsshkey { + if key.UserID.String() != userID.String() { + continue + } + q.gitsshkey[index] = q.gitsshkey[len(q.gitsshkey)-1] + q.gitsshkey = q.gitsshkey[:len(q.gitsshkey)-1] + return nil + } + return sql.ErrNoRows +} diff --git a/coderd/database/models.go b/coderd/database/models.go index 6e027bbda64e4..15ba8944df56d 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -256,7 +256,7 @@ type File struct { } type GitSshKey struct { - UserID string `db:"user_id" json:"user_id"` + UserID uuid.UUID `db:"user_id" json:"user_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` PrivateKey string `db:"private_key" json:"private_key"` diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 1625c64c16a85..36a776fd2b67d 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -1,6 +1,7 @@ package coderd import ( + "fmt" "net/http" "github.com/go-chi/render" @@ -28,8 +29,8 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { err = api.Database.UpdateGitSSHKey(r.Context(), database.UpdateGitSSHKeyParams{ UserID: user.ID, UpdatedAt: database.Now(), - PrivateKey: string(privateKey), - PublicKey: string(publicKey), + PrivateKey: privateKey, + PublicKey: publicKey, }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ @@ -67,5 +68,48 @@ func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { } func (api *api) privateGitSSHKey(rw http.ResponseWriter, r *http.Request) { - // connect agent to workspace to user to gitsshkey + var ( + agent = httpmw.WorkspaceAgent(r) + ) + + resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("getting workspace resources: %s", err), + }) + return + } + + job, err := api.Database.GetWorkspaceBuildByJobID(r.Context(), resource.JobID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("getting workspace build: %s", err), + }) + return + } + + workspace, err := api.Database.GetWorkspaceByID(r.Context(), job.WorkspaceID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("getting workspace: %s", err), + }) + return + } + + gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), workspace.OwnerID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: fmt.Sprintf("getting git ssh key: %s", err), + }) + return + } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, codersdk.AgentGitSSHKey{ + UserID: gitSSHKey.UserID, + CreatedAt: gitSSHKey.CreatedAt, + UpdatedAt: gitSSHKey.UpdatedAt, + PrivateKey: gitSSHKey.PrivateKey, + PublicKey: gitSSHKey.PublicKey, + }) } diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go index f4681503c5925..7728c484da4a1 100644 --- a/coderd/gitsshkey/gitsshkey.go +++ b/coderd/gitsshkey/gitsshkey.go @@ -25,7 +25,8 @@ const ( AlgorithmRSA4096 Algorithm = "rsa4096" ) -func GenerateKeyPair(algo Algorithm) ([]byte, []byte, error) { +// nolint: revive +func GenerateKeyPair(algo Algorithm) (string, string, error) { switch algo { case AlgorithmEd25519: return ed25519KeyGen() @@ -34,15 +35,15 @@ func GenerateKeyPair(algo Algorithm) ([]byte, []byte, error) { case AlgorithmRSA4096: return rsa4096KeyGen() default: - return nil, nil, xerrors.Errorf("invalid algorithm: %s", algo) + return "", "", xerrors.Errorf("invalid algorithm: %s", algo) } } // ed25519KeyGen returns an ED25519-based SSH private key. -func ed25519KeyGen() ([]byte, []byte, error) { - publicKey, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) +func ed25519KeyGen() (privateKey string, publicKey string, err error) { + publicKeyRaw, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) if err != nil { - return nil, nil, xerrors.Errorf("generate ed25519 private key: %w", err) + return "", "", xerrors.Errorf("generate ed25519 private key: %w", err) } // NOTE: as of the time of writing, x/crypto/ssh is unable to marshal an ED25519 private key @@ -50,7 +51,7 @@ func ed25519KeyGen() ([]byte, []byte, error) { // Until this support is added, using a third-party implementation. byt, err := MarshalED25519PrivateKey(privateKeyRaw) if err != nil { - return nil, nil, xerrors.Errorf("marshal ed25519 private key: %w", err) + return "", "", xerrors.Errorf("marshal ed25519 private key: %w", err) } pb := pem.Block{ @@ -58,25 +59,26 @@ func ed25519KeyGen() ([]byte, []byte, error) { Headers: nil, Bytes: byt, } - privateKey := pem.EncodeToMemory(&pb) + privateKey = string(pem.EncodeToMemory(&pb)) + publicKey = string(publicKeyRaw) return privateKey, publicKey, nil } // ecdsaKeyGen returns an ECDSA-based SSH private key. -func ecdsaKeyGen() ([]byte, []byte, error) { +func ecdsaKeyGen() (privateKey string, publicKey string, err error) { privateKeyRaw, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { - return nil, nil, xerrors.Errorf("generate ecdsa private key: %w", err) + return "", "", xerrors.Errorf("generate ecdsa private key: %w", err) } - publicKey, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) + publicKeyRaw, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) if err != nil { - return nil, nil, xerrors.Errorf("generate ecdsa public key: %w", err) + return "", "", xerrors.Errorf("generate ecdsa public key: %w", err) } byt, err := x509.MarshalECPrivateKey(privateKeyRaw) if err != nil { - return nil, nil, xerrors.Errorf("marshal private key: %w", err) + return "", "", xerrors.Errorf("marshal private key: %w", err) } pb := pem.Block{ @@ -84,7 +86,8 @@ func ecdsaKeyGen() ([]byte, []byte, error) { Headers: nil, Bytes: byt, } - privateKey := pem.EncodeToMemory(&pb) + privateKey = string(pem.EncodeToMemory(&pb)) + publicKey = string(publicKeyRaw) return privateKey, publicKey, nil } @@ -92,21 +95,22 @@ func ecdsaKeyGen() ([]byte, []byte, error) { // rsaKeyGen returns an RSA-based SSH private key of size 4096. // // Administrators may configure this for SSH key compatibility with Azure DevOps. -func rsa4096KeyGen() ([]byte, []byte, error) { +func rsa4096KeyGen() (privateKey string, publicKey string, err error) { privateKeyRaw, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { - return nil, nil, xerrors.Errorf("generate RSA4096 private key: %w", err) + return "", "", xerrors.Errorf("generate RSA4096 private key: %w", err) } - publicKey, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) + publicKeyRaw, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) if err != nil { - return nil, nil, xerrors.Errorf("generate RSA4096 public key: %w", err) + return "", "", xerrors.Errorf("generate RSA4096 public key: %w", err) } pb := pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKeyRaw), } - privateKey := pem.EncodeToMemory(&pb) + privateKey = string(pem.EncodeToMemory(&pb)) + publicKey = string(publicKeyRaw) return privateKey, publicKey, nil } @@ -125,5 +129,5 @@ func ParseSSHKeygenAlgorithm(t string) (Algorithm, error) { } } - return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join([]string(ok), ",")) + return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join(ok, ",")) } diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 9b9aff2c57f68..7cfcff70a36b8 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -1,10 +1,22 @@ package codersdk -import "time" +import ( + "time" + + "github.com/google/uuid" +) type GitSSHKey struct { - UserID string `json:"user_id"` + UserID uuid.UUID `json:"user_id"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PublicKey string `json:"public_key"` } + +type AgentGitSSHKey struct { + UserID uuid.UUID `json:"user_id"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + PrivateKey string `json:"private_key"` + PublicKey string `json:"public_key"` +} From f755cdb42db4316e5af6fc6e7e7a2f2783baa15b Mon Sep 17 00:00:00 2001 From: Garrett Date: Mon, 4 Apr 2022 20:58:42 +0000 Subject: [PATCH 11/20] add agent route --- coderd/coderd.go | 1 + coderd/gitsshkey.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index c04029bf9c479..bba5d1addbb0a 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -152,6 +152,7 @@ func New(options *Options) (http.Handler, func()) { r.Route("/agent", func(r chi.Router) { r.Use(httpmw.ExtractWorkspaceAgent(options.Database)) r.Get("/", api.workspaceAgentListen) + r.Get("/gitsshkey", api.agentGitSSHKey) }) r.Route("/{workspaceresource}", func(r chi.Router) { r.Use( diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 36a776fd2b67d..f43fe487c8436 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -67,7 +67,7 @@ func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { }) } -func (api *api) privateGitSSHKey(rw http.ResponseWriter, r *http.Request) { +func (api *api) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { var ( agent = httpmw.WorkspaceAgent(r) ) From 45dc1fbe37ffe648f5d4a6a4c285e5b1fde052b3 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 20:35:52 +0000 Subject: [PATCH 12/20] add tests for gitsshkey package --- coderd/coderdtest/coderdtest.go | 3 +- coderd/gitsshkey/gitsshkey.go | 60 ++++++++++++++++----------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index 8a016325c1d8c..c5c769836e9a4 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -109,7 +109,8 @@ func New(t *testing.T, options *Options) *codersdk.Client { AWSCertificates: options.AWSInstanceIdentity, GoogleTokenValidator: options.GoogleInstanceIdentity, - SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519, + // Default to none so we don't burn a ton of CPU in tests + SSHKeygenAlgorithm: gitsshkey.AlgorithmNone, }) t.Cleanup(func() { srv.Close() diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go index 7728c484da4a1..39bd27b2ea32c 100644 --- a/coderd/gitsshkey/gitsshkey.go +++ b/coderd/gitsshkey/gitsshkey.go @@ -1,6 +1,7 @@ package gitsshkey import ( + "crypto" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" @@ -10,6 +11,7 @@ import ( "encoding/pem" "strings" + "golang.org/x/crypto/ssh" "golang.org/x/xerrors" ) @@ -23,6 +25,8 @@ const ( // AlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm // and creates a key with a fixed size of 4096-bit. AlgorithmRSA4096 Algorithm = "rsa4096" + // AlgorithmNone will return empty keys. + AlgorithmNone Algorithm = "none" ) // nolint: revive @@ -34,6 +38,8 @@ func GenerateKeyPair(algo Algorithm) (string, string, error) { return ecdsaKeyGen() case AlgorithmRSA4096: return rsa4096KeyGen() + case AlgorithmNone: + return "", "", nil default: return "", "", xerrors.Errorf("invalid algorithm: %s", algo) } @@ -41,7 +47,7 @@ func GenerateKeyPair(algo Algorithm) (string, string, error) { // ed25519KeyGen returns an ED25519-based SSH private key. func ed25519KeyGen() (privateKey string, publicKey string, err error) { - publicKeyRaw, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) + _, privateKeyRaw, err := ed25519.GenerateKey(rand.Reader) if err != nil { return "", "", xerrors.Errorf("generate ed25519 private key: %w", err) } @@ -54,15 +60,10 @@ func ed25519KeyGen() (privateKey string, publicKey string, err error) { return "", "", xerrors.Errorf("marshal ed25519 private key: %w", err) } - pb := pem.Block{ - Type: "OPENSSH PRIVATE KEY", - Headers: nil, - Bytes: byt, - } - privateKey = string(pem.EncodeToMemory(&pb)) - publicKey = string(publicKeyRaw) - - return privateKey, publicKey, nil + return generateKeys(pem.Block{ + Type: "OPENSSH PRIVATE KEY", + Bytes: byt, + }, privateKeyRaw) } // ecdsaKeyGen returns an ECDSA-based SSH private key. @@ -71,25 +72,15 @@ func ecdsaKeyGen() (privateKey string, publicKey string, err error) { if err != nil { return "", "", xerrors.Errorf("generate ecdsa private key: %w", err) } - publicKeyRaw, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) - if err != nil { - return "", "", xerrors.Errorf("generate ecdsa public key: %w", err) - } - byt, err := x509.MarshalECPrivateKey(privateKeyRaw) if err != nil { return "", "", xerrors.Errorf("marshal private key: %w", err) } - pb := pem.Block{ - Type: "EC PRIVATE KEY", - Headers: nil, - Bytes: byt, - } - privateKey = string(pem.EncodeToMemory(&pb)) - publicKey = string(publicKeyRaw) - - return privateKey, publicKey, nil + return generateKeys(pem.Block{ + Type: "EC PRIVATE KEY", + Bytes: byt, + }, privateKeyRaw) } // rsaKeyGen returns an RSA-based SSH private key of size 4096. @@ -100,17 +91,23 @@ func rsa4096KeyGen() (privateKey string, publicKey string, err error) { if err != nil { return "", "", xerrors.Errorf("generate RSA4096 private key: %w", err) } - publicKeyRaw, err := x509.MarshalPKIXPublicKey(privateKeyRaw.PublicKey) - if err != nil { - return "", "", xerrors.Errorf("generate RSA4096 public key: %w", err) - } - pb := pem.Block{ + return generateKeys(pem.Block{ Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKeyRaw), + }, privateKeyRaw) +} + +func generateKeys(block pem.Block, cp crypto.Signer) (privateKey string, publicKey string, err error) { + pkBytes := pem.EncodeToMemory(&block) + privateKey = string(pkBytes) + + publicKeyRaw := cp.Public() + p, err := ssh.NewPublicKey(publicKeyRaw) + if err != nil { + return "", "", err } - privateKey = string(pem.EncodeToMemory(&pb)) - publicKey = string(publicKeyRaw) + publicKey = string(ssh.MarshalAuthorizedKey(p)) return privateKey, publicKey, nil } @@ -121,6 +118,7 @@ func ParseSSHKeygenAlgorithm(t string) (Algorithm, error) { string(AlgorithmEd25519), string(AlgorithmECDSA), string(AlgorithmRSA4096), + string(AlgorithmNone), } for _, a := range ok { From ab87f127584fcd014c4c89efd9ab1d00b251d51d Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 21:25:18 +0000 Subject: [PATCH 13/20] add gitsshkey coderd tests --- coderd/coderdtest/coderdtest.go | 10 ++- coderd/gitsshkey/gitsshkey_test.go | 59 +++++++++++++ coderd/gitsshkey_test.go | 130 +++++++++++++++++++++++++++++ codersdk/gitsshkey.go | 50 +++++++++++ 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 coderd/gitsshkey/gitsshkey_test.go create mode 100644 coderd/gitsshkey_test.go diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index c5c769836e9a4..f640ce52f1f9d 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -50,6 +50,7 @@ import ( type Options struct { AWSInstanceIdentity awsidentity.Certificates GoogleInstanceIdentity *idtoken.Validator + SSHKeygenAlgorithm gitsshkey.Algorithm } // New constructs an in-memory coderd instance and returns @@ -99,6 +100,12 @@ func New(t *testing.T, options *Options) *codersdk.Client { serverURL, err := url.Parse(srv.URL) require.NoError(t, err) var closeWait func() + + // Default to none so we don't burn a ton of CPU in tests + if options.SSHKeygenAlgorithm == "" { + options.SSHKeygenAlgorithm = gitsshkey.AlgorithmNone + } + // We set the handler after server creation for the access URL. srv.Config.Handler, closeWait = coderd.New(&coderd.Options{ AgentConnectionUpdateFrequency: 25 * time.Millisecond, @@ -109,8 +116,7 @@ func New(t *testing.T, options *Options) *codersdk.Client { AWSCertificates: options.AWSInstanceIdentity, GoogleTokenValidator: options.GoogleInstanceIdentity, - // Default to none so we don't burn a ton of CPU in tests - SSHKeygenAlgorithm: gitsshkey.AlgorithmNone, + SSHKeygenAlgorithm: options.SSHKeygenAlgorithm, }) t.Cleanup(func() { srv.Close() diff --git a/coderd/gitsshkey/gitsshkey_test.go b/coderd/gitsshkey/gitsshkey_test.go new file mode 100644 index 0000000000000..459ab9cfcaf9d --- /dev/null +++ b/coderd/gitsshkey/gitsshkey_test.go @@ -0,0 +1,59 @@ +package gitsshkey_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "golang.org/x/crypto/ssh" + + "github.com/coder/coder/coderd/gitsshkey" + "github.com/coder/coder/cryptorand" +) + +func TestGitSSHKeys(t *testing.T) { + verifyKeyPair := func(t *testing.T, private, public string) { + signer, err := ssh.ParsePrivateKey([]byte(private)) + require.NoError(t, err) + p, err := ssh.ParsePublicKey(signer.PublicKey().Marshal()) + require.NoError(t, err) + publicKey := string(ssh.MarshalAuthorizedKey(p)) + require.Equal(t, publicKey, public) + } + + t.Run("None", func(t *testing.T) { + pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmNone) + require.NoError(t, err) + require.Empty(t, pv) + require.Empty(t, pb) + }) + t.Run("Ed25519", func(t *testing.T) { + pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmEd25519) + require.NoError(t, err) + verifyKeyPair(t, pv, pb) + }) + t.Run("ECDSA", func(t *testing.T) { + pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmECDSA) + require.NoError(t, err) + verifyKeyPair(t, pv, pb) + }) + t.Run("RSA4096", func(t *testing.T) { + pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmRSA4096) + require.NoError(t, err) + verifyKeyPair(t, pv, pb) + }) + t.Run("ParseAlgorithm", func(t *testing.T) { + _, err := gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmNone)) + require.NoError(t, err) + _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmEd25519)) + require.NoError(t, err) + _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmECDSA)) + require.NoError(t, err) + _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmRSA4096)) + require.NoError(t, err) + r, _ := cryptorand.String(6) + _, err = gitsshkey.ParseSSHKeygenAlgorithm(r) + require.Error(t, err, "random string should fail") + _, err = gitsshkey.ParseSSHKeygenAlgorithm("") + require.Error(t, err, "empty string should fail") + }) +} diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go new file mode 100644 index 0000000000000..17e3b8795e0fc --- /dev/null +++ b/coderd/gitsshkey_test.go @@ -0,0 +1,130 @@ +package coderd_test + +import ( + "context" + "testing" + + "github.com/coder/coder/coderd/coderdtest" + "github.com/coder/coder/coderd/gitsshkey" + "github.com/coder/coder/codersdk" + "github.com/coder/coder/provisioner/echo" + "github.com/coder/coder/provisionersdk/proto" + "github.com/google/uuid" + "github.com/stretchr/testify/require" +) + +func TestGitSSHKey(t *testing.T) { + t.Parallel() + t.Run("None", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := coderdtest.New(t, nil) + res := coderdtest.CreateFirstUser(t, client) + key, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) + require.Empty(t, key.PublicKey) + }) + t.Run("Ed25519", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := coderdtest.New(t, &coderdtest.Options{ + SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519, + }) + res := coderdtest.CreateFirstUser(t, client) + key, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) + require.NotEmpty(t, key.PublicKey) + }) + t.Run("ECDSA", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := coderdtest.New(t, &coderdtest.Options{ + SSHKeygenAlgorithm: gitsshkey.AlgorithmECDSA, + }) + res := coderdtest.CreateFirstUser(t, client) + key, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) + require.NotEmpty(t, key.PublicKey) + }) + t.Run("RSA4096", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := coderdtest.New(t, &coderdtest.Options{ + SSHKeygenAlgorithm: gitsshkey.AlgorithmRSA4096, + }) + res := coderdtest.CreateFirstUser(t, client) + key, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) + require.NotEmpty(t, key.PublicKey) + }) + t.Run("Regenerate", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := coderdtest.New(t, &coderdtest.Options{ + SSHKeygenAlgorithm: gitsshkey.AlgorithmEd25519, + }) + res := coderdtest.CreateFirstUser(t, client) + key1, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) + require.NotEmpty(t, key1.PublicKey) + err = client.RegenerateGitSSHKey(ctx, res.UserID) + require.NoError(t, err) + key2, err := client.GitSSHKey(ctx, res.UserID) + require.Greater(t, key2.UpdatedAt, key1.UpdatedAt) + require.NotEmpty(t, key2.PublicKey) + require.NotEqual(t, key2.PublicKey, key1.PublicKey) + }) +} + +func TestAgentGitSSHKey(t *testing.T) { + t.Parallel() + + agentClient := func(algo gitsshkey.Algorithm) *codersdk.Client { + client := coderdtest.New(t, &coderdtest.Options{ + SSHKeygenAlgorithm: algo, + }) + user := coderdtest.CreateFirstUser(t, client) + daemonCloser := coderdtest.NewProvisionerDaemon(t, client) + authToken := uuid.NewString() + version := coderdtest.CreateProjectVersion(t, client, user.OrganizationID, &echo.Responses{ + Parse: echo.ParseComplete, + ProvisionDryRun: echo.ProvisionComplete, + Provision: []*proto.Provision_Response{{ + Type: &proto.Provision_Response_Complete{ + Complete: &proto.Provision_Complete{ + Resources: []*proto.Resource{{ + Name: "example", + Type: "aws_instance", + Agent: &proto.Agent{ + Id: uuid.NewString(), + Auth: &proto.Agent_Token{ + Token: authToken, + }, + }, + }}, + }, + }, + }}, + }) + project := coderdtest.CreateProject(t, client, user.OrganizationID, version.ID) + coderdtest.AwaitProjectVersionJob(t, client, version.ID) + workspace := coderdtest.CreateWorkspace(t, client, codersdk.Me, project.ID) + coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID) + daemonCloser.Close() + + agentClient := codersdk.New(client.URL) + agentClient.SessionToken = authToken + + return agentClient + } + + t.Run("AgentKey", func(t *testing.T) { + t.Parallel() + ctx := context.Background() + client := agentClient(gitsshkey.AlgorithmEd25519) + agentKey, err := client.AgentGitSSHKey(ctx) + require.NoError(t, err) + require.NotEmpty(t, agentKey.PrivateKey) + require.NotEmpty(t, agentKey.PublicKey) + }) +} diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 7cfcff70a36b8..eff3f1e168b38 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -1,9 +1,14 @@ package codersdk import ( + "context" + "encoding/json" + "fmt" + "net/http" "time" "github.com/google/uuid" + "golang.org/x/xerrors" ) type GitSSHKey struct { @@ -20,3 +25,48 @@ type AgentGitSSHKey struct { PrivateKey string `json:"private_key"` PublicKey string `json:"public_key"` } + +// GitSSHKey returns the user +func (c *Client) GitSSHKey(ctx context.Context, userId uuid.UUID) (GitSSHKey, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userId.String()), nil) + if err != nil { + return GitSSHKey{}, xerrors.Errorf("execute request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return GitSSHKey{}, readBodyAsError(res) + } + + var gitsshkey GitSSHKey + return gitsshkey, json.NewDecoder(res.Body).Decode(&gitsshkey) +} + +func (c *Client) RegenerateGitSSHKey(ctx context.Context, userId uuid.UUID) error { + res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userId.String()), nil) + if err != nil { + return xerrors.Errorf("execute request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return readBodyAsError(res) + } + + return nil +} + +func (c *Client) AgentGitSSHKey(ctx context.Context) (AgentGitSSHKey, error) { + res, err := c.request(ctx, http.MethodGet, "/api/v2/workspaceresources/agent/gitsshkey", nil) + if err != nil { + return AgentGitSSHKey{}, xerrors.Errorf("execute request: %w", err) + } + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return AgentGitSSHKey{}, readBodyAsError(res) + } + + var agentgitsshkey AgentGitSSHKey + return agentgitsshkey, json.NewDecoder(res.Body).Decode(&agentgitsshkey) +} From c7b81cc2f8e1f4fb7678cac26cf95c39dce0b272 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 21:44:32 +0000 Subject: [PATCH 14/20] fix proto --- peerbroker/proto/peerbroker.pb.go | 2 +- provisionerd/proto/provisionerd.pb.go | 2 +- provisionersdk/proto/provisioner.pb.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/peerbroker/proto/peerbroker.pb.go b/peerbroker/proto/peerbroker.pb.go index 8a443e6e42192..b1a880bf8ce36 100644 --- a/peerbroker/proto/peerbroker.pb.go +++ b/peerbroker/proto/peerbroker.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: peerbroker/proto/peerbroker.proto package proto diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index aace1aa426ea6..782bccb8c955f 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: provisionerd/proto/provisionerd.proto package proto diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 61f9beef2e055..c5617f74cdfca 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.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: provisionersdk/proto/provisioner.proto package proto From fc4fad544b43d3344c290138bc35820e99864418 Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 21:49:44 +0000 Subject: [PATCH 15/20] lint --- coderd/gitsshkey/gitsshkey_test.go | 7 +++++++ coderd/gitsshkey_test.go | 6 ++++-- codersdk/gitsshkey.go | 8 ++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/coderd/gitsshkey/gitsshkey_test.go b/coderd/gitsshkey/gitsshkey_test.go index 459ab9cfcaf9d..79941f4600ffd 100644 --- a/coderd/gitsshkey/gitsshkey_test.go +++ b/coderd/gitsshkey/gitsshkey_test.go @@ -11,6 +11,8 @@ import ( ) func TestGitSSHKeys(t *testing.T) { + t.Parallel() + verifyKeyPair := func(t *testing.T, private, public string) { signer, err := ssh.ParsePrivateKey([]byte(private)) require.NoError(t, err) @@ -21,27 +23,32 @@ func TestGitSSHKeys(t *testing.T) { } t.Run("None", func(t *testing.T) { + t.Parallel() pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmNone) require.NoError(t, err) require.Empty(t, pv) require.Empty(t, pb) }) t.Run("Ed25519", func(t *testing.T) { + t.Parallel() pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmEd25519) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("ECDSA", func(t *testing.T) { + t.Parallel() pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmECDSA) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("RSA4096", func(t *testing.T) { + t.Parallel() pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmRSA4096) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("ParseAlgorithm", func(t *testing.T) { + t.Parallel() _, err := gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmNone)) require.NoError(t, err) _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmEd25519)) diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go index 17e3b8795e0fc..a9fe7b5a18a8c 100644 --- a/coderd/gitsshkey_test.go +++ b/coderd/gitsshkey_test.go @@ -4,13 +4,14 @@ import ( "context" "testing" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "github.com/coder/coder/coderd/coderdtest" "github.com/coder/coder/coderd/gitsshkey" "github.com/coder/coder/codersdk" "github.com/coder/coder/provisioner/echo" "github.com/coder/coder/provisionersdk/proto" - "github.com/google/uuid" - "github.com/stretchr/testify/require" ) func TestGitSSHKey(t *testing.T) { @@ -70,6 +71,7 @@ func TestGitSSHKey(t *testing.T) { err = client.RegenerateGitSSHKey(ctx, res.UserID) require.NoError(t, err) key2, err := client.GitSSHKey(ctx, res.UserID) + require.NoError(t, err) require.Greater(t, key2.UpdatedAt, key1.UpdatedAt) require.NotEmpty(t, key2.PublicKey) require.NotEqual(t, key2.PublicKey, key1.PublicKey) diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index eff3f1e168b38..094c4da6cec22 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -27,8 +27,8 @@ type AgentGitSSHKey struct { } // GitSSHKey returns the user -func (c *Client) GitSSHKey(ctx context.Context, userId uuid.UUID) (GitSSHKey, error) { - res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userId.String()), nil) +func (c *Client) GitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) { + res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) if err != nil { return GitSSHKey{}, xerrors.Errorf("execute request: %w", err) } @@ -42,8 +42,8 @@ func (c *Client) GitSSHKey(ctx context.Context, userId uuid.UUID) (GitSSHKey, er return gitsshkey, json.NewDecoder(res.Body).Decode(&gitsshkey) } -func (c *Client) RegenerateGitSSHKey(ctx context.Context, userId uuid.UUID) error { - res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userId.String()), nil) +func (c *Client) RegenerateGitSSHKey(ctx context.Context, userID uuid.UUID) error { + res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) if err != nil { return xerrors.Errorf("execute request: %w", err) } From 6c83c549b9d00dc70a552418b53554c018f3544b Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 23:05:27 +0000 Subject: [PATCH 16/20] pr comments --- coderd/coderdtest/coderdtest.go | 4 ++-- coderd/gitsshkey.go | 23 ++++++++++++++++++----- coderd/gitsshkey/gitsshkey.go | 12 ++++-------- coderd/gitsshkey/gitsshkey_test.go | 11 +---------- coderd/gitsshkey_test.go | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/coderd/coderdtest/coderdtest.go b/coderd/coderdtest/coderdtest.go index f640ce52f1f9d..1a4df644a1ee5 100644 --- a/coderd/coderdtest/coderdtest.go +++ b/coderd/coderdtest/coderdtest.go @@ -101,9 +101,9 @@ func New(t *testing.T, options *Options) *codersdk.Client { require.NoError(t, err) var closeWait func() - // Default to none so we don't burn a ton of CPU in tests + // match default with cli default if options.SSHKeygenAlgorithm == "" { - options.SSHKeygenAlgorithm = gitsshkey.AlgorithmNone + options.SSHKeygenAlgorithm = gitsshkey.AlgorithmEd25519 } // We set the handler after server creation for the access URL. diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index f43fe487c8436..5ce9bea6d07e4 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -34,13 +34,26 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not update git ssh key.", + Message: "Could not update git SSH key.", }) return } - httpapi.Write(rw, http.StatusOK, httpapi.Response{ - Message: "Updated git ssh key!", + newKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID) + if err != nil { + httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ + Message: "Could not update git SSH key.", + }) + return + } + + render.Status(r, http.StatusOK) + render.JSON(rw, r, codersdk.GitSSHKey{ + UserID: newKey.UserID, + CreatedAt: newKey.CreatedAt, + UpdatedAt: newKey.UpdatedAt, + // No need to return the private key to the user + PublicKey: newKey.PublicKey, }) } @@ -52,7 +65,7 @@ func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not update git ssh key.", + Message: "Could not update git SSH key.", }) return } @@ -99,7 +112,7 @@ func (api *api) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), workspace.OwnerID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: fmt.Sprintf("getting git ssh key: %s", err), + Message: fmt.Sprintf("getting git SSH key: %s", err), }) return } diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go index 39bd27b2ea32c..a26059701225b 100644 --- a/coderd/gitsshkey/gitsshkey.go +++ b/coderd/gitsshkey/gitsshkey.go @@ -25,12 +25,11 @@ const ( // AlgorithmRSA4096 is the venerable Rivest-Shamir-Adleman algorithm // and creates a key with a fixed size of 4096-bit. AlgorithmRSA4096 Algorithm = "rsa4096" - // AlgorithmNone will return empty keys. - AlgorithmNone Algorithm = "none" ) -// nolint: revive -func GenerateKeyPair(algo Algorithm) (string, string, error) { +// GenerateKeyPair creates a private key in the OpenSSH PEM format and public key in +// the authorized key format. +func GenerateKeyPair(algo Algorithm) (privateKey string, publicKey string, err error) { switch algo { case AlgorithmEd25519: return ed25519KeyGen() @@ -38,8 +37,6 @@ func GenerateKeyPair(algo Algorithm) (string, string, error) { return ecdsaKeyGen() case AlgorithmRSA4096: return rsa4096KeyGen() - case AlgorithmNone: - return "", "", nil default: return "", "", xerrors.Errorf("invalid algorithm: %s", algo) } @@ -118,11 +115,10 @@ func ParseSSHKeygenAlgorithm(t string) (Algorithm, error) { string(AlgorithmEd25519), string(AlgorithmECDSA), string(AlgorithmRSA4096), - string(AlgorithmNone), } for _, a := range ok { - if t == a { + if strings.EqualFold(a, t) { return Algorithm(a), nil } } diff --git a/coderd/gitsshkey/gitsshkey_test.go b/coderd/gitsshkey/gitsshkey_test.go index 79941f4600ffd..2617b88240d30 100644 --- a/coderd/gitsshkey/gitsshkey_test.go +++ b/coderd/gitsshkey/gitsshkey_test.go @@ -22,13 +22,6 @@ func TestGitSSHKeys(t *testing.T) { require.Equal(t, publicKey, public) } - t.Run("None", func(t *testing.T) { - t.Parallel() - pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmNone) - require.NoError(t, err) - require.Empty(t, pv) - require.Empty(t, pb) - }) t.Run("Ed25519", func(t *testing.T) { t.Parallel() pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmEd25519) @@ -49,9 +42,7 @@ func TestGitSSHKeys(t *testing.T) { }) t.Run("ParseAlgorithm", func(t *testing.T) { t.Parallel() - _, err := gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmNone)) - require.NoError(t, err) - _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmEd25519)) + _, err := gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmEd25519)) require.NoError(t, err) _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmECDSA)) require.NoError(t, err) diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go index a9fe7b5a18a8c..1c0f965597128 100644 --- a/coderd/gitsshkey_test.go +++ b/coderd/gitsshkey_test.go @@ -23,7 +23,7 @@ func TestGitSSHKey(t *testing.T) { res := coderdtest.CreateFirstUser(t, client) key, err := client.GitSSHKey(ctx, res.UserID) require.NoError(t, err) - require.Empty(t, key.PublicKey) + require.NotEmpty(t, key.PublicKey) }) t.Run("Ed25519", func(t *testing.T) { t.Parallel() From c265a3cf56d7433ed19c7700049832638cda581c Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 23:11:25 +0000 Subject: [PATCH 17/20] change regenerate to a PUT method --- coderd/coderd.go | 2 +- codersdk/gitsshkey.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index bba5d1addbb0a..f2290e23dde42 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -140,7 +140,7 @@ func New(options *Options) (http.Handler, func()) { r.Get("/{workspacename}", api.workspaceByUserAndName) }) r.Get("/gitsshkey", api.gitSSHKey) - r.Post("/gitsshkey", api.regenerateGitSSHKey) + r.Put("/gitsshkey", api.regenerateGitSSHKey) }) }) }) diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 094c4da6cec22..57522a73a57cf 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -43,7 +43,7 @@ func (c *Client) GitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, er } func (c *Client) RegenerateGitSSHKey(ctx context.Context, userID uuid.UUID) error { - res, err := c.request(ctx, http.MethodPost, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) + res, err := c.request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) if err != nil { return xerrors.Errorf("execute request: %w", err) } From 89bf77ca5481933f255eadaa1f65d31bda53a02f Mon Sep 17 00:00:00 2001 From: Garrett Date: Tue, 5 Apr 2022 23:16:09 +0000 Subject: [PATCH 18/20] Fix codersdk --- coderd/gitsshkey_test.go | 4 +--- codersdk/gitsshkey.go | 13 ++++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go index 1c0f965597128..f96d4271e815e 100644 --- a/coderd/gitsshkey_test.go +++ b/coderd/gitsshkey_test.go @@ -68,9 +68,7 @@ func TestGitSSHKey(t *testing.T) { key1, err := client.GitSSHKey(ctx, res.UserID) require.NoError(t, err) require.NotEmpty(t, key1.PublicKey) - err = client.RegenerateGitSSHKey(ctx, res.UserID) - require.NoError(t, err) - key2, err := client.GitSSHKey(ctx, res.UserID) + key2, err := client.RegenerateGitSSHKey(ctx, res.UserID) require.NoError(t, err) require.Greater(t, key2.UpdatedAt, key1.UpdatedAt) require.NotEmpty(t, key2.PublicKey) diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 57522a73a57cf..658c1b4e4fbb1 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -26,7 +26,7 @@ type AgentGitSSHKey struct { PublicKey string `json:"public_key"` } -// GitSSHKey returns the user +// GitSSHKey returns the user's git SSH public key. func (c *Client) GitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) { res, err := c.request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) if err != nil { @@ -42,20 +42,23 @@ func (c *Client) GitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, er return gitsshkey, json.NewDecoder(res.Body).Decode(&gitsshkey) } -func (c *Client) RegenerateGitSSHKey(ctx context.Context, userID uuid.UUID) error { +// RegenerateGitSSHKey will create a new SSH key pair for the user and return it. +func (c *Client) RegenerateGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) { res, err := c.request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/gitsshkey", userID.String()), nil) if err != nil { - return xerrors.Errorf("execute request: %w", err) + return GitSSHKey{}, xerrors.Errorf("execute request: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { - return readBodyAsError(res) + return GitSSHKey{}, readBodyAsError(res) } - return nil + var gitsshkey GitSSHKey + return gitsshkey, json.NewDecoder(res.Body).Decode(&gitsshkey) } +// AgentGitSSHKey will return the user's SSH key pair for the workspace. func (c *Client) AgentGitSSHKey(ctx context.Context) (AgentGitSSHKey, error) { res, err := c.request(ctx, http.MethodGet, "/api/v2/workspaceresources/agent/gitsshkey", nil) if err != nil { From f310c7184f82bd3d52e43079ccc62a81f2bb700c Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 6 Apr 2022 00:03:55 +0000 Subject: [PATCH 19/20] pr comments --- cli/start.go | 2 +- coderd/database/databasefake/databasefake.go | 28 +++++++------- coderd/database/dump.sql | 10 ++--- .../migrations/000005_gitsshkey.down.sql | 2 +- .../migrations/000005_gitsshkey.up.sql | 2 +- coderd/database/models.go | 2 +- coderd/database/querier.go | 4 +- coderd/database/queries.sql.go | 16 ++++---- coderd/database/queries/gitsshkeys.sql | 8 ++-- coderd/database/sqlc.yaml | 1 + coderd/gitsshkey.go | 26 ++++--------- coderd/gitsshkey/gitsshkey.go | 38 +++++++++---------- coderd/gitsshkey/gitsshkey_test.go | 16 ++++---- coderd/gitsshkey_test.go | 1 - coderd/users.go | 4 +- codersdk/gitsshkey.go | 1 - peerbroker/proto/peerbroker.pb.go | 2 +- provisionerd/proto/provisionerd.pb.go | 2 +- provisionersdk/proto/provisioner.pb.go | 2 +- 19 files changed, 78 insertions(+), 89 deletions(-) diff --git a/cli/start.go b/cli/start.go index 1d262b96a51db..43876f0a6d09f 100644 --- a/cli/start.go +++ b/cli/start.go @@ -129,7 +129,7 @@ func start() *cobra.Command { return xerrors.Errorf("parse access url %q: %w", accessURL, err) } - sshKeygenAlgorithm, err := gitsshkey.ParseSSHKeygenAlgorithm(sshKeygenAlgorithmRaw) + sshKeygenAlgorithm, err := gitsshkey.ParseAlgorithm(sshKeygenAlgorithmRaw) if err != nil { return xerrors.Errorf("parse ssh keygen algorithm %s: %w", sshKeygenAlgorithmRaw, err) } diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index a1198c9522fc8..ce0ad771b7c1e 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -31,7 +31,7 @@ func New() database.Store { provisionerJobResource: make([]database.WorkspaceResource, 0), workspaceBuild: make([]database.WorkspaceBuild, 0), provisionerJobAgent: make([]database.WorkspaceAgent, 0), - gitsshkey: make([]database.GitSshKey, 0), + GitSSHKey: make([]database.GitSSHKey, 0), } } @@ -58,7 +58,7 @@ type fakeQuerier struct { provisionerJobLog []database.ProvisionerJobLog workspace []database.Workspace workspaceBuild []database.WorkspaceBuild - gitsshkey []database.GitSshKey + GitSSHKey []database.GitSSHKey } // InTx doesn't rollback data properly for in-memory yet. @@ -1242,46 +1242,46 @@ func (q *fakeQuerier) UpdateWorkspaceDeletedByID(_ context.Context, arg database return sql.ErrNoRows } -func (q *fakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitSSHKeyParams) (database.GitSshKey, error) { +func (q *fakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitSSHKeyParams) (database.GitSSHKey, error) { q.mutex.Lock() defer q.mutex.Unlock() //nolint:gosimple - gitsshkey := database.GitSshKey{ + GitSSHKey := database.GitSSHKey{ UserID: arg.UserID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, PrivateKey: arg.PrivateKey, PublicKey: arg.PublicKey, } - q.gitsshkey = append(q.gitsshkey, gitsshkey) - return gitsshkey, nil + q.GitSSHKey = append(q.GitSSHKey, GitSSHKey) + return GitSSHKey, nil } -func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSshKey, error) { +func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSSHKey, error) { q.mutex.RLock() defer q.mutex.RUnlock() - for _, key := range q.gitsshkey { + for _, key := range q.GitSSHKey { if key.UserID == userID { return key, nil } } - return database.GitSshKey{}, sql.ErrNoRows + return database.GitSSHKey{}, sql.ErrNoRows } func (q *fakeQuerier) UpdateGitSSHKey(_ context.Context, arg database.UpdateGitSSHKeyParams) error { q.mutex.Lock() defer q.mutex.Unlock() - for index, key := range q.gitsshkey { + for index, key := range q.GitSSHKey { if key.UserID.String() != arg.UserID.String() { continue } key.UpdatedAt = arg.UpdatedAt key.PrivateKey = arg.PrivateKey key.PublicKey = arg.PublicKey - q.gitsshkey[index] = key + q.GitSSHKey[index] = key return nil } return sql.ErrNoRows @@ -1291,12 +1291,12 @@ func (q *fakeQuerier) DeleteGitSSHKey(_ context.Context, userID uuid.UUID) error q.mutex.Lock() defer q.mutex.Unlock() - for index, key := range q.gitsshkey { + for index, key := range q.GitSSHKey { if key.UserID.String() != userID.String() { continue } - q.gitsshkey[index] = q.gitsshkey[len(q.gitsshkey)-1] - q.gitsshkey = q.gitsshkey[:len(q.gitsshkey)-1] + q.GitSSHKey[index] = q.GitSSHKey[len(q.GitSSHKey)-1] + q.GitSSHKey = q.GitSSHKey[:len(q.GitSSHKey)-1] return nil } return sql.ErrNoRows diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 0d0c7395be69c..8df6305c7d37a 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -89,7 +89,7 @@ CREATE TABLE files ( data bytea NOT NULL ); -CREATE TABLE git_ssh_keys ( +CREATE TABLE gitsshkeys ( user_id uuid NOT NULL, created_at timestamp with time zone NOT NULL, updated_at timestamp with time zone NOT NULL, @@ -291,8 +291,8 @@ ALTER TABLE ONLY api_keys ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (hash); -ALTER TABLE ONLY git_ssh_keys - ADD CONSTRAINT git_ssh_keys_pkey PRIMARY KEY (user_id); +ALTER TABLE ONLY gitsshkeys + ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); @@ -390,8 +390,8 @@ CREATE UNIQUE INDEX workspaces_owner_id_name_idx ON workspaces USING btree (owne ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; -ALTER TABLE ONLY git_ssh_keys - ADD CONSTRAINT git_ssh_keys_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id); +ALTER TABLE ONLY gitsshkeys + ADD CONSTRAINT gitsshkeys_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id); ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; diff --git a/coderd/database/migrations/000005_gitsshkey.down.sql b/coderd/database/migrations/000005_gitsshkey.down.sql index e54f051f85310..29d97a70f2c54 100644 --- a/coderd/database/migrations/000005_gitsshkey.down.sql +++ b/coderd/database/migrations/000005_gitsshkey.down.sql @@ -1 +1 @@ -DROP TABLE git_ssh_keys; +DROP TABLE gitsshkeys; diff --git a/coderd/database/migrations/000005_gitsshkey.up.sql b/coderd/database/migrations/000005_gitsshkey.up.sql index cfc9fb0c4c0b8..efb59e8f484e9 100644 --- a/coderd/database/migrations/000005_gitsshkey.up.sql +++ b/coderd/database/migrations/000005_gitsshkey.up.sql @@ -1,4 +1,4 @@ -CREATE TABLE IF NOT EXISTS git_ssh_keys ( +CREATE TABLE IF NOT EXISTS gitsshkeys ( user_id uuid PRIMARY KEY NOT NULL REFERENCES users (id), created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL, diff --git a/coderd/database/models.go b/coderd/database/models.go index 15ba8944df56d..26517c03b68cc 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -255,7 +255,7 @@ type File struct { Data []byte `db:"data" json:"data"` } -type GitSshKey struct { +type GitSSHKey struct { UserID uuid.UUID `db:"user_id" json:"user_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index b761c42b15b46..9582490162cc0 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -14,7 +14,7 @@ type querier interface { DeleteParameterValueByID(ctx context.Context, id uuid.UUID) error GetAPIKeyByID(ctx context.Context, id string) (APIKey, error) GetFileByHash(ctx context.Context, hash string) (File, error) - GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSshKey, error) + GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) GetOrganizationMemberByUserID(ctx context.Context, arg GetOrganizationMemberByUserIDParams) (OrganizationMember, error) @@ -56,7 +56,7 @@ type querier interface { GetWorkspacesByUserID(ctx context.Context, arg GetWorkspacesByUserIDParams) ([]Workspace, error) InsertAPIKey(ctx context.Context, arg InsertAPIKeyParams) (APIKey, error) InsertFile(ctx context.Context, arg InsertFileParams) (File, error) - InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) + InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSSHKey, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) InsertParameterSchema(ctx context.Context, arg InsertParameterSchemaParams) (ParameterSchema, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6e5b4f8a5782b..85855a1ead817 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -237,7 +237,7 @@ func (q *sqlQuerier) InsertFile(ctx context.Context, arg InsertFileParams) (File const deleteGitSSHKey = `-- name: DeleteGitSSHKey :exec DELETE FROM - git_ssh_keys + gitsshkeys WHERE user_id = $1 ` @@ -251,14 +251,14 @@ const getGitSSHKey = `-- name: GetGitSSHKey :one SELECT user_id, created_at, updated_at, private_key, public_key FROM - git_ssh_keys + gitsshkeys WHERE user_id = $1 ` -func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSshKey, error) { +func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSSHKey, error) { row := q.db.QueryRowContext(ctx, getGitSSHKey, userID) - var i GitSshKey + var i GitSSHKey err := row.Scan( &i.UserID, &i.CreatedAt, @@ -271,7 +271,7 @@ func (q *sqlQuerier) GetGitSSHKey(ctx context.Context, userID uuid.UUID) (GitSsh const insertGitSSHKey = `-- name: InsertGitSSHKey :one INSERT INTO - git_ssh_keys ( + gitsshkeys ( user_id, created_at, updated_at, @@ -290,7 +290,7 @@ type InsertGitSSHKeyParams struct { PublicKey string `db:"public_key" json:"public_key"` } -func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSshKey, error) { +func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyParams) (GitSSHKey, error) { row := q.db.QueryRowContext(ctx, insertGitSSHKey, arg.UserID, arg.CreatedAt, @@ -298,7 +298,7 @@ func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyPar arg.PrivateKey, arg.PublicKey, ) - var i GitSshKey + var i GitSSHKey err := row.Scan( &i.UserID, &i.CreatedAt, @@ -311,7 +311,7 @@ func (q *sqlQuerier) InsertGitSSHKey(ctx context.Context, arg InsertGitSSHKeyPar const updateGitSSHKey = `-- name: UpdateGitSSHKey :exec UPDATE - git_ssh_keys + gitsshkeys SET updated_at = $2, private_key = $3, diff --git a/coderd/database/queries/gitsshkeys.sql b/coderd/database/queries/gitsshkeys.sql index 4bc6dd5afcf5b..1fe9c97fa16ff 100644 --- a/coderd/database/queries/gitsshkeys.sql +++ b/coderd/database/queries/gitsshkeys.sql @@ -1,6 +1,6 @@ -- name: InsertGitSSHKey :one INSERT INTO - git_ssh_keys ( + gitsshkeys ( user_id, created_at, updated_at, @@ -14,13 +14,13 @@ VALUES SELECT * FROM - git_ssh_keys + gitsshkeys WHERE user_id = $1; -- name: UpdateGitSSHKey :exec UPDATE - git_ssh_keys + gitsshkeys SET updated_at = $2, private_key = $3, @@ -30,6 +30,6 @@ WHERE -- name: DeleteGitSSHKey :exec DELETE FROM - git_ssh_keys + gitsshkeys WHERE user_id = $1; diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 8248ad8a72259..a009644cdf520 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -27,3 +27,4 @@ rename: oidc_refresh_token: OIDCRefreshToken parameter_type_system_hcl: ParameterTypeSystemHCL userstatus: UserStatus + gitsshkey: GitSSHKey diff --git a/coderd/gitsshkey.go b/coderd/gitsshkey.go index 5ce9bea6d07e4..743101c23beae 100644 --- a/coderd/gitsshkey.go +++ b/coderd/gitsshkey.go @@ -14,14 +14,11 @@ import ( ) func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { - var ( - user = httpmw.UserParam(r) - ) - - privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + user := httpmw.UserParam(r) + privateKey, publicKey, err := gitsshkey.Generate(api.SSHKeygenAlgorithm) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not regenerate key pair.", + Message: fmt.Sprintf("regenerate key pair: %s", err), }) return } @@ -34,7 +31,7 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { }) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not update git SSH key.", + Message: fmt.Sprintf("update git SSH key: %s", err), }) return } @@ -42,7 +39,7 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { newKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not update git SSH key.", + Message: fmt.Sprintf("get git SSH key: %s", err), }) return } @@ -58,14 +55,11 @@ func (api *api) regenerateGitSSHKey(rw http.ResponseWriter, r *http.Request) { } func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { - var ( - user = httpmw.UserParam(r) - ) - + user := httpmw.UserParam(r) gitSSHKey, err := api.Database.GetGitSSHKey(r.Context(), user.ID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ - Message: "Could not update git SSH key.", + Message: fmt.Sprintf("update git SSH key: %s", err), }) return } @@ -81,10 +75,7 @@ func (api *api) gitSSHKey(rw http.ResponseWriter, r *http.Request) { } func (api *api) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { - var ( - agent = httpmw.WorkspaceAgent(r) - ) - + agent := httpmw.WorkspaceAgent(r) resource, err := api.Database.GetWorkspaceResourceByID(r.Context(), agent.ResourceID) if err != nil { httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ @@ -123,6 +114,5 @@ func (api *api) agentGitSSHKey(rw http.ResponseWriter, r *http.Request) { CreatedAt: gitSSHKey.CreatedAt, UpdatedAt: gitSSHKey.UpdatedAt, PrivateKey: gitSSHKey.PrivateKey, - PublicKey: gitSSHKey.PublicKey, }) } diff --git a/coderd/gitsshkey/gitsshkey.go b/coderd/gitsshkey/gitsshkey.go index a26059701225b..f122205b1e6e5 100644 --- a/coderd/gitsshkey/gitsshkey.go +++ b/coderd/gitsshkey/gitsshkey.go @@ -27,9 +27,26 @@ const ( AlgorithmRSA4096 Algorithm = "rsa4096" ) -// GenerateKeyPair creates a private key in the OpenSSH PEM format and public key in +// ParseAlgorithm returns a valid Algorithm or error if input is not a valid. +func ParseAlgorithm(t string) (Algorithm, error) { + ok := []string{ + string(AlgorithmEd25519), + string(AlgorithmECDSA), + string(AlgorithmRSA4096), + } + + for _, a := range ok { + if strings.EqualFold(a, t) { + return Algorithm(a), nil + } + } + + return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join(ok, ",")) +} + +// Generate creates a private key in the OpenSSH PEM format and public key in // the authorized key format. -func GenerateKeyPair(algo Algorithm) (privateKey string, publicKey string, err error) { +func Generate(algo Algorithm) (privateKey string, publicKey string, err error) { switch algo { case AlgorithmEd25519: return ed25519KeyGen() @@ -108,20 +125,3 @@ func generateKeys(block pem.Block, cp crypto.Signer) (privateKey string, publicK return privateKey, publicKey, nil } - -// ParseSSHKeygenAlgorithm returns a valid SSHKeygenAlgorithm or error if input is not a valid. -func ParseSSHKeygenAlgorithm(t string) (Algorithm, error) { - ok := []string{ - string(AlgorithmEd25519), - string(AlgorithmECDSA), - string(AlgorithmRSA4096), - } - - for _, a := range ok { - if strings.EqualFold(a, t) { - return Algorithm(a), nil - } - } - - return "", xerrors.Errorf(`invalid key type: %s, must be one of: %s`, t, strings.Join(ok, ",")) -} diff --git a/coderd/gitsshkey/gitsshkey_test.go b/coderd/gitsshkey/gitsshkey_test.go index 2617b88240d30..df9502ee43955 100644 --- a/coderd/gitsshkey/gitsshkey_test.go +++ b/coderd/gitsshkey/gitsshkey_test.go @@ -24,34 +24,34 @@ func TestGitSSHKeys(t *testing.T) { t.Run("Ed25519", func(t *testing.T) { t.Parallel() - pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmEd25519) + pv, pb, err := gitsshkey.Generate(gitsshkey.AlgorithmEd25519) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("ECDSA", func(t *testing.T) { t.Parallel() - pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmECDSA) + pv, pb, err := gitsshkey.Generate(gitsshkey.AlgorithmECDSA) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("RSA4096", func(t *testing.T) { t.Parallel() - pv, pb, err := gitsshkey.GenerateKeyPair(gitsshkey.AlgorithmRSA4096) + pv, pb, err := gitsshkey.Generate(gitsshkey.AlgorithmRSA4096) require.NoError(t, err) verifyKeyPair(t, pv, pb) }) t.Run("ParseAlgorithm", func(t *testing.T) { t.Parallel() - _, err := gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmEd25519)) + _, err := gitsshkey.ParseAlgorithm(string(gitsshkey.AlgorithmEd25519)) require.NoError(t, err) - _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmECDSA)) + _, err = gitsshkey.ParseAlgorithm(string(gitsshkey.AlgorithmECDSA)) require.NoError(t, err) - _, err = gitsshkey.ParseSSHKeygenAlgorithm(string(gitsshkey.AlgorithmRSA4096)) + _, err = gitsshkey.ParseAlgorithm(string(gitsshkey.AlgorithmRSA4096)) require.NoError(t, err) r, _ := cryptorand.String(6) - _, err = gitsshkey.ParseSSHKeygenAlgorithm(r) + _, err = gitsshkey.ParseAlgorithm(r) require.Error(t, err, "random string should fail") - _, err = gitsshkey.ParseSSHKeygenAlgorithm("") + _, err = gitsshkey.ParseAlgorithm("") require.Error(t, err, "empty string should fail") }) } diff --git a/coderd/gitsshkey_test.go b/coderd/gitsshkey_test.go index f96d4271e815e..8748b2fcb7aab 100644 --- a/coderd/gitsshkey_test.go +++ b/coderd/gitsshkey_test.go @@ -125,6 +125,5 @@ func TestAgentGitSSHKey(t *testing.T) { agentKey, err := client.AgentGitSSHKey(ctx) require.NoError(t, err) require.NotEmpty(t, agentKey.PrivateKey) - require.NotEmpty(t, agentKey.PublicKey) }) } diff --git a/coderd/users.go b/coderd/users.go index 223fe64d3c29b..e490ce5d08e3a 100644 --- a/coderd/users.go +++ b/coderd/users.go @@ -95,7 +95,7 @@ func (api *api) postFirstUser(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("create user: %w", err) } - privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + privateKey, publicKey, err := gitsshkey.Generate(api.SSHKeygenAlgorithm) if err != nil { return xerrors.Errorf("generate user gitsshkey: %w", err) } @@ -224,7 +224,7 @@ func (api *api) postUsers(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("create user: %w", err) } - privateKey, publicKey, err := gitsshkey.GenerateKeyPair(api.SSHKeygenAlgorithm) + privateKey, publicKey, err := gitsshkey.Generate(api.SSHKeygenAlgorithm) if err != nil { return xerrors.Errorf("generate user gitsshkey: %w", err) } diff --git a/codersdk/gitsshkey.go b/codersdk/gitsshkey.go index 658c1b4e4fbb1..faade33da90a9 100644 --- a/codersdk/gitsshkey.go +++ b/codersdk/gitsshkey.go @@ -23,7 +23,6 @@ type AgentGitSSHKey struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` PrivateKey string `json:"private_key"` - PublicKey string `json:"public_key"` } // GitSSHKey returns the user's git SSH public key. diff --git a/peerbroker/proto/peerbroker.pb.go b/peerbroker/proto/peerbroker.pb.go index b1a880bf8ce36..8a443e6e42192 100644 --- a/peerbroker/proto/peerbroker.pb.go +++ b/peerbroker/proto/peerbroker.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: peerbroker/proto/peerbroker.proto package proto diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index 782bccb8c955f..aace1aa426ea6 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: provisionerd/proto/provisionerd.proto package proto diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index c5617f74cdfca..61f9beef2e055 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.26.0 -// protoc v3.19.4 +// protoc v3.6.1 // source: provisionersdk/proto/provisioner.proto package proto From dee1968380f1f394644bf6c50f493e9349908dbc Mon Sep 17 00:00:00 2001 From: Garrett Date: Wed, 6 Apr 2022 00:09:15 +0000 Subject: [PATCH 20/20] fix --- coderd/database/databasefake/databasefake.go | 6 +++--- peerbroker/proto/peerbroker.pb.go | 2 +- provisionerd/proto/provisionerd.pb.go | 2 +- provisionersdk/proto/provisioner.pb.go | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/coderd/database/databasefake/databasefake.go b/coderd/database/databasefake/databasefake.go index ce0ad771b7c1e..5003843cfa561 100644 --- a/coderd/database/databasefake/databasefake.go +++ b/coderd/database/databasefake/databasefake.go @@ -1247,15 +1247,15 @@ func (q *fakeQuerier) InsertGitSSHKey(_ context.Context, arg database.InsertGitS defer q.mutex.Unlock() //nolint:gosimple - GitSSHKey := database.GitSSHKey{ + gitSSHKey := database.GitSSHKey{ UserID: arg.UserID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, PrivateKey: arg.PrivateKey, PublicKey: arg.PublicKey, } - q.GitSSHKey = append(q.GitSSHKey, GitSSHKey) - return GitSSHKey, nil + q.GitSSHKey = append(q.GitSSHKey, gitSSHKey) + return gitSSHKey, nil } func (q *fakeQuerier) GetGitSSHKey(_ context.Context, userID uuid.UUID) (database.GitSSHKey, error) { diff --git a/peerbroker/proto/peerbroker.pb.go b/peerbroker/proto/peerbroker.pb.go index 8a443e6e42192..b1a880bf8ce36 100644 --- a/peerbroker/proto/peerbroker.pb.go +++ b/peerbroker/proto/peerbroker.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: peerbroker/proto/peerbroker.proto package proto diff --git a/provisionerd/proto/provisionerd.pb.go b/provisionerd/proto/provisionerd.pb.go index aace1aa426ea6..782bccb8c955f 100644 --- a/provisionerd/proto/provisionerd.pb.go +++ b/provisionerd/proto/provisionerd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: provisionerd/proto/provisionerd.proto package proto diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 61f9beef2e055..c5617f74cdfca 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.26.0 -// protoc v3.6.1 +// protoc v3.19.4 // source: provisionersdk/proto/provisioner.proto package proto