chore: move app proxying code to workspaceapps pkg (#6998)

* chore: move app proxying code to workspaceapps pkg

Moves path-app, subdomain-app and reconnecting PTY proxying to the new
workspaceapps.WorkspaceAppServer struct. This is in preparation for
external workspace proxies.

Updates app logout flow to avoid redirecting to coder-logout.${app_host}
on logout. Instead, all subdomain app tokens owned by the logging-out
user will be deleted every time you logout for simplicity sake.

Tests will remain in their original package, pending being moved to an
apptest package (or similar).

Co-authored-by: Steven Masley <stevenmasley@coder.com>
This commit is contained in:
Dean Sheather
2023-04-06 04:41:55 +10:00
committed by GitHub
parent 0069831e8d
commit eb66cc9f35
28 changed files with 1236 additions and 1334 deletions

View File

@ -379,14 +379,14 @@ func (q *querier) GetLogoURL(ctx context.Context) (string, error) {
return q.db.GetLogoURL(ctx)
}
func (q *querier) GetAppSigningKey(ctx context.Context) (string, error) {
func (q *querier) GetAppSecurityKey(ctx context.Context) (string, error) {
// No authz checks
return q.db.GetAppSigningKey(ctx)
return q.db.GetAppSecurityKey(ctx)
}
func (q *querier) InsertAppSigningKey(ctx context.Context, data string) error {
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
// No authz checks as this is done during startup
return q.db.InsertAppSigningKey(ctx, data)
return q.db.UpsertAppSecurityKey(ctx, data)
}
func (q *querier) GetServiceBanner(ctx context.Context) (string, error) {
@ -994,6 +994,16 @@ func (q *querier) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]dat
return q.db.GetTemplateUserRoles(ctx, id)
}
func (q *querier) DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error {
// TODO: This is not 100% correct because it omits apikey IDs.
err := q.authorizeContext(ctx, rbac.ActionDelete,
rbac.ResourceAPIKey.WithOwner(userID.String()))
if err != nil {
return err
}
return q.db.DeleteApplicationConnectAPIKeysByUserID(ctx, userID)
}
func (q *querier) DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error {
// TODO: This is not 100% correct because it omits apikey IDs.
err := q.authorizeContext(ctx, rbac.ActionDelete,

View File

@ -143,7 +143,7 @@ type data struct {
lastUpdateCheck []byte
serviceBanner []byte
logoURL string
appSigningKey string
appSecurityKey string
lastLicenseID int32
}
@ -679,6 +679,19 @@ func (q *fakeQuerier) DeleteAPIKeyByID(_ context.Context, id string) error {
return sql.ErrNoRows
}
func (q *fakeQuerier) DeleteApplicationConnectAPIKeysByUserID(_ context.Context, userID uuid.UUID) error {
q.mutex.Lock()
defer q.mutex.Unlock()
for i := len(q.apiKeys) - 1; i >= 0; i-- {
if q.apiKeys[i].UserID == userID && q.apiKeys[i].Scope == database.APIKeyScopeApplicationConnect {
q.apiKeys = append(q.apiKeys[:i], q.apiKeys[i+1:]...)
}
}
return nil
}
func (q *fakeQuerier) DeleteAPIKeysByUserID(_ context.Context, userID uuid.UUID) error {
q.mutex.Lock()
defer q.mutex.Unlock()
@ -4463,18 +4476,18 @@ func (q *fakeQuerier) GetLogoURL(_ context.Context) (string, error) {
return q.logoURL, nil
}
func (q *fakeQuerier) GetAppSigningKey(_ context.Context) (string, error) {
func (q *fakeQuerier) GetAppSecurityKey(_ context.Context) (string, error) {
q.mutex.RLock()
defer q.mutex.RUnlock()
return q.appSigningKey, nil
return q.appSecurityKey, nil
}
func (q *fakeQuerier) InsertAppSigningKey(_ context.Context, data string) error {
func (q *fakeQuerier) UpsertAppSecurityKey(_ context.Context, data string) error {
q.mutex.Lock()
defer q.mutex.Unlock()
q.appSigningKey = data
q.appSecurityKey = data
return nil
}

View File

@ -77,12 +77,23 @@ func APIKey(t testing.TB, db database.Store, seed database.APIKey) (key database
secret, _ := cryptorand.String(22)
hashed := sha256.Sum256([]byte(secret))
ip := seed.IPAddress
if !ip.Valid {
ip = pqtype.Inet{
IPNet: net.IPNet{
IP: net.IPv4(127, 0, 0, 1),
Mask: net.IPv4Mask(255, 255, 255, 255),
},
Valid: true,
}
}
key, err := db.InsertAPIKey(context.Background(), database.InsertAPIKeyParams{
ID: takeFirst(seed.ID, id),
// 0 defaults to 86400 at the db layer
LifetimeSeconds: takeFirst(seed.LifetimeSeconds, 0),
HashedSecret: takeFirstSlice(seed.HashedSecret, hashed[:]),
IPAddress: pqtype.Inet{},
IPAddress: ip,
UserID: takeFirst(seed.UserID, uuid.New()),
LastUsed: takeFirst(seed.LastUsed, database.Now()),
ExpiresAt: takeFirst(seed.ExpiresAt, database.Now().Add(time.Hour)),

View File

@ -28,6 +28,7 @@ type sqlcQuerier interface {
AcquireProvisionerJob(ctx context.Context, arg AcquireProvisionerJobParams) (ProvisionerJob, error)
DeleteAPIKeyByID(ctx context.Context, id string) error
DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error
DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error
DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error
DeleteGroupByID(ctx context.Context, id uuid.UUID) error
DeleteGroupMemberFromGroup(ctx context.Context, arg DeleteGroupMemberFromGroupParams) error
@ -46,7 +47,7 @@ type sqlcQuerier interface {
GetAPIKeysByUserID(ctx context.Context, arg GetAPIKeysByUserIDParams) ([]APIKey, error)
GetAPIKeysLastUsedAfter(ctx context.Context, lastUsed time.Time) ([]APIKey, error)
GetActiveUserCount(ctx context.Context) (int64, error)
GetAppSigningKey(ctx context.Context) (string, error)
GetAppSecurityKey(ctx context.Context) (string, error)
// GetAuditLogsBefore retrieves `row_limit` number of audit logs before the provided
// ID.
GetAuditLogsOffset(ctx context.Context, arg GetAuditLogsOffsetParams) ([]GetAuditLogsOffsetRow, error)
@ -161,7 +162,6 @@ type sqlcQuerier interface {
// for simplicity since all users is
// every member of the org.
InsertAllUsersGroup(ctx context.Context, organizationID uuid.UUID) (Group, error)
InsertAppSigningKey(ctx context.Context, value string) error
InsertAuditLog(ctx context.Context, arg InsertAuditLogParams) (AuditLog, error)
InsertDERPMeshKey(ctx context.Context, value string) error
InsertDeploymentID(ctx context.Context, value string) error
@ -248,6 +248,7 @@ type sqlcQuerier interface {
UpdateWorkspaceProxyDeleted(ctx context.Context, arg UpdateWorkspaceProxyDeletedParams) error
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
UpdateWorkspaceTTLToBeWithinTemplateMax(ctx context.Context, arg UpdateWorkspaceTTLToBeWithinTemplateMaxParams) error
UpsertAppSecurityKey(ctx context.Context, value string) error
UpsertLastUpdateCheck(ctx context.Context, value string) error
UpsertLogoURL(ctx context.Context, value string) error
UpsertServiceBanner(ctx context.Context, value string) error

View File

@ -17,8 +17,7 @@ import (
)
const deleteAPIKeyByID = `-- name: DeleteAPIKeyByID :exec
DELETE
FROM
DELETE FROM
api_keys
WHERE
id = $1
@ -41,6 +40,19 @@ func (q *sqlQuerier) DeleteAPIKeysByUserID(ctx context.Context, userID uuid.UUID
return err
}
const deleteApplicationConnectAPIKeysByUserID = `-- name: DeleteApplicationConnectAPIKeysByUserID :exec
DELETE FROM
api_keys
WHERE
user_id = $1 AND
scope = 'application_connect'::api_key_scope
`
func (q *sqlQuerier) DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error {
_, err := q.db.ExecContext(ctx, deleteApplicationConnectAPIKeysByUserID, userID)
return err
}
const getAPIKeyByID = `-- name: GetAPIKeyByID :one
SELECT
id, hashed_secret, user_id, last_used, expires_at, created_at, updated_at, login_type, lifetime_seconds, ip_address, scope, token_name
@ -3202,12 +3214,12 @@ func (q *sqlQuerier) UpdateReplica(ctx context.Context, arg UpdateReplicaParams)
return i, err
}
const getAppSigningKey = `-- name: GetAppSigningKey :one
const getAppSecurityKey = `-- name: GetAppSecurityKey :one
SELECT value FROM site_configs WHERE key = 'app_signing_key'
`
func (q *sqlQuerier) GetAppSigningKey(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getAppSigningKey)
func (q *sqlQuerier) GetAppSecurityKey(ctx context.Context) (string, error) {
row := q.db.QueryRowContext(ctx, getAppSecurityKey)
var value string
err := row.Scan(&value)
return value, err
@ -3268,15 +3280,6 @@ func (q *sqlQuerier) GetServiceBanner(ctx context.Context) (string, error) {
return value, err
}
const insertAppSigningKey = `-- name: InsertAppSigningKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
`
func (q *sqlQuerier) InsertAppSigningKey(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, insertAppSigningKey, value)
return err
}
const insertDERPMeshKey = `-- name: InsertDERPMeshKey :exec
INSERT INTO site_configs (key, value) VALUES ('derp_mesh_key', $1)
`
@ -3295,6 +3298,16 @@ func (q *sqlQuerier) InsertDeploymentID(ctx context.Context, value string) error
return err
}
const upsertAppSecurityKey = `-- name: UpsertAppSecurityKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'app_signing_key'
`
func (q *sqlQuerier) UpsertAppSecurityKey(ctx context.Context, value string) error {
_, err := q.db.ExecContext(ctx, upsertAppSecurityKey, value)
return err
}
const upsertLastUpdateCheck = `-- name: UpsertLastUpdateCheck :exec
INSERT INTO site_configs (key, value) VALUES ('last_update_check', $1)
ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'last_update_check'

View File

@ -66,12 +66,18 @@ WHERE
id = $1;
-- name: DeleteAPIKeyByID :exec
DELETE
FROM
DELETE FROM
api_keys
WHERE
id = $1;
-- name: DeleteApplicationConnectAPIKeysByUserID :exec
DELETE FROM
api_keys
WHERE
user_id = $1 AND
scope = 'application_connect'::api_key_scope;
-- name: DeleteAPIKeysByUserID :exec
DELETE FROM
api_keys

View File

@ -31,8 +31,9 @@ ON CONFLICT (key) DO UPDATE SET value = $1 WHERE site_configs.key = 'logo_url';
-- name: GetLogoURL :one
SELECT value FROM site_configs WHERE key = 'logo_url';
-- name: GetAppSigningKey :one
-- name: GetAppSecurityKey :one
SELECT value FROM site_configs WHERE key = 'app_signing_key';
-- name: InsertAppSigningKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1);
-- name: UpsertAppSecurityKey :exec
INSERT INTO site_configs (key, value) VALUES ('app_signing_key', $1)
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'app_signing_key';