mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add schema for key rotation (#14662)
This commit is contained in:
@@ -253,6 +253,7 @@ var (
|
||||
rbac.ResourceDeploymentConfig.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
|
||||
rbac.ResourceNotificationPreference.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
|
||||
rbac.ResourceNotificationTemplate.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
|
||||
rbac.ResourceCryptoKey.Type: {policy.ActionCreate, policy.ActionUpdate, policy.ActionDelete},
|
||||
}),
|
||||
Org: map[string][]rbac.Permission{},
|
||||
User: []rbac.Permission{},
|
||||
@@ -1041,6 +1042,13 @@ func (q *querier) DeleteCoordinator(ctx context.Context, id uuid.UUID) error {
|
||||
return q.db.DeleteCoordinator(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteCryptoKey(ctx context.Context, arg database.DeleteCryptoKeyParams) (database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceCryptoKey); err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
return q.db.DeleteCryptoKey(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteCustomRole(ctx context.Context, arg database.DeleteCustomRoleParams) error {
|
||||
if arg.OrganizationID.UUID != uuid.Nil {
|
||||
if err := q.authorizeContext(ctx, policy.ActionDelete, rbac.ResourceAssignOrgRole.InOrg(arg.OrganizationID.UUID)); err != nil {
|
||||
@@ -1383,6 +1391,20 @@ func (q *querier) GetCoordinatorResumeTokenSigningKey(ctx context.Context) (stri
|
||||
return q.db.GetCoordinatorResumeTokenSigningKey(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetCryptoKeyByFeatureAndSequence(ctx context.Context, arg database.GetCryptoKeyByFeatureAndSequenceParams) (database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceCryptoKey); err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
return q.db.GetCryptoKeyByFeatureAndSequence(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetCryptoKeys(ctx context.Context) ([]database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceCryptoKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetCryptoKeys(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetDBCryptKeys(ctx context.Context) ([]database.DBCryptKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
@@ -1549,6 +1571,13 @@ func (q *querier) GetLastUpdateCheck(ctx context.Context) (string, error) {
|
||||
return q.db.GetLastUpdateCheck(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetLatestCryptoKeyByFeature(ctx context.Context, feature database.CryptoKeyFeature) (database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceCryptoKey); err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
return q.db.GetLatestCryptoKeyByFeature(ctx, feature)
|
||||
}
|
||||
|
||||
func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
if _, err := q.GetWorkspaceByID(ctx, workspaceID); err != nil {
|
||||
return database.WorkspaceBuild{}, err
|
||||
@@ -2662,6 +2691,13 @@ func (q *querier) InsertAuditLog(ctx context.Context, arg database.InsertAuditLo
|
||||
return insert(q.log, q.auth, rbac.ResourceAuditLog, q.db.InsertAuditLog)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceCryptoKey); err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
return q.db.InsertCryptoKey(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertCustomRole(ctx context.Context, arg database.InsertCustomRoleParams) (database.CustomRole, error) {
|
||||
// Org and site role upsert share the same query. So switch the assertion based on the org uuid.
|
||||
if arg.OrganizationID.UUID != uuid.Nil {
|
||||
@@ -3169,6 +3205,13 @@ func (q *querier) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateAPIKe
|
||||
return update(q.log, q.auth, fetch, q.db.UpdateAPIKeyByID)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateCryptoKeyDeletesAt(ctx context.Context, arg database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceCryptoKey); err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
return q.db.UpdateCryptoKeyDeletesAt(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateCustomRole(ctx context.Context, arg database.UpdateCustomRoleParams) (database.CustomRole, error) {
|
||||
if arg.OrganizationID.UUID != uuid.Nil {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceAssignOrgRole.InOrg(arg.OrganizationID.UUID)); err != nil {
|
||||
|
||||
@@ -2253,6 +2253,57 @@ func (s *MethodTestSuite) TestDBCrypt() {
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestCryptoKeys() {
|
||||
s.Run("GetCryptoKeys", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args().
|
||||
Asserts(rbac.ResourceCryptoKey, policy.ActionRead)
|
||||
}))
|
||||
s.Run("InsertCryptoKey", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(database.InsertCryptoKeyParams{
|
||||
Feature: database.CryptoKeyFeatureWorkspaceApps,
|
||||
}).
|
||||
Asserts(rbac.ResourceCryptoKey, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("DeleteCryptoKey", s.Subtest(func(db database.Store, check *expects) {
|
||||
key := dbgen.CryptoKey(s.T(), db, database.CryptoKey{
|
||||
Feature: database.CryptoKeyFeatureWorkspaceApps,
|
||||
Sequence: 4,
|
||||
})
|
||||
check.Args(database.DeleteCryptoKeyParams{
|
||||
Feature: key.Feature,
|
||||
Sequence: key.Sequence,
|
||||
}).Asserts(rbac.ResourceCryptoKey, policy.ActionDelete)
|
||||
}))
|
||||
s.Run("GetCryptoKeyByFeatureAndSequence", s.Subtest(func(db database.Store, check *expects) {
|
||||
key := dbgen.CryptoKey(s.T(), db, database.CryptoKey{
|
||||
Feature: database.CryptoKeyFeatureWorkspaceApps,
|
||||
Sequence: 4,
|
||||
})
|
||||
check.Args(database.GetCryptoKeyByFeatureAndSequenceParams{
|
||||
Feature: key.Feature,
|
||||
Sequence: key.Sequence,
|
||||
}).Asserts(rbac.ResourceCryptoKey, policy.ActionRead).Returns(key)
|
||||
}))
|
||||
s.Run("GetLatestCryptoKeyByFeature", s.Subtest(func(db database.Store, check *expects) {
|
||||
dbgen.CryptoKey(s.T(), db, database.CryptoKey{
|
||||
Feature: database.CryptoKeyFeatureWorkspaceApps,
|
||||
Sequence: 4,
|
||||
})
|
||||
check.Args(database.CryptoKeyFeatureWorkspaceApps).Asserts(rbac.ResourceCryptoKey, policy.ActionRead)
|
||||
}))
|
||||
s.Run("UpdateCryptoKeyDeletesAt", s.Subtest(func(db database.Store, check *expects) {
|
||||
key := dbgen.CryptoKey(s.T(), db, database.CryptoKey{
|
||||
Feature: database.CryptoKeyFeatureWorkspaceApps,
|
||||
Sequence: 4,
|
||||
})
|
||||
check.Args(database.UpdateCryptoKeyDeletesAtParams{
|
||||
Feature: key.Feature,
|
||||
Sequence: key.Sequence,
|
||||
DeletesAt: sql.NullTime{Time: time.Now(), Valid: true},
|
||||
}).Asserts(rbac.ResourceCryptoKey, policy.ActionUpdate)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
s.Run("UpdateUserLinkedID", s.Subtest(func(db database.Store, check *expects) {
|
||||
u := dbgen.User(s.T(), db, database.User{})
|
||||
|
||||
@@ -2,6 +2,7 @@ package dbgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/sqlc-dev/pqtype"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
@@ -893,6 +895,40 @@ func CustomRole(t testing.TB, db database.Store, seed database.CustomRole) datab
|
||||
return role
|
||||
}
|
||||
|
||||
func CryptoKey(t testing.TB, db database.Store, seed database.CryptoKey) database.CryptoKey {
|
||||
t.Helper()
|
||||
|
||||
seed.Feature = takeFirst(seed.Feature, database.CryptoKeyFeatureWorkspaceApps)
|
||||
|
||||
if !seed.Secret.Valid {
|
||||
secret, err := newCryptoKeySecret(seed.Feature)
|
||||
require.NoError(t, err, "generate secret")
|
||||
seed.Secret = sql.NullString{
|
||||
String: secret,
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
key, err := db.InsertCryptoKey(genCtx, database.InsertCryptoKeyParams{
|
||||
Sequence: takeFirst(seed.Sequence, 123),
|
||||
Secret: seed.Secret,
|
||||
SecretKeyID: takeFirst(seed.SecretKeyID, sql.NullString{}),
|
||||
Feature: seed.Feature,
|
||||
StartsAt: takeFirst(seed.StartsAt, time.Now()),
|
||||
})
|
||||
require.NoError(t, err, "insert crypto key")
|
||||
|
||||
if seed.DeletesAt.Valid {
|
||||
key, err = db.UpdateCryptoKeyDeletesAt(genCtx, database.UpdateCryptoKeyDeletesAtParams{
|
||||
Feature: key.Feature,
|
||||
Sequence: key.Sequence,
|
||||
DeletesAt: sql.NullTime{Time: seed.DeletesAt.Time, Valid: true},
|
||||
})
|
||||
require.NoError(t, err, "update crypto key deletes_at")
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func ProvisionerJobTimings(t testing.TB, db database.Store, seed database.InsertProvisionerJobTimingsParams) []database.ProvisionerJobTiming {
|
||||
timings, err := db.InsertProvisionerJobTimings(genCtx, seed)
|
||||
require.NoError(t, err, "insert provisioner job timings")
|
||||
@@ -942,3 +978,24 @@ func takeFirst[Value comparable](values ...Value) Value {
|
||||
return v != empty
|
||||
})
|
||||
}
|
||||
|
||||
func newCryptoKeySecret(feature database.CryptoKeyFeature) (string, error) {
|
||||
switch feature {
|
||||
case database.CryptoKeyFeatureWorkspaceApps:
|
||||
return generateCryptoKey(96)
|
||||
case database.CryptoKeyFeatureOidcConvert:
|
||||
return generateCryptoKey(32)
|
||||
case database.CryptoKeyFeatureTailnetResume:
|
||||
return generateCryptoKey(64)
|
||||
}
|
||||
return "", xerrors.Errorf("unknown feature: %s", feature)
|
||||
}
|
||||
|
||||
func generateCryptoKey(length int) (string, error) {
|
||||
b := make([]byte, length)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("rand read: %w", err)
|
||||
}
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
@@ -189,6 +189,7 @@ type data struct {
|
||||
// New tables
|
||||
workspaceAgentStats []database.WorkspaceAgentStat
|
||||
auditLogs []database.AuditLog
|
||||
cryptoKeys []database.CryptoKey
|
||||
dbcryptKeys []database.DBCryptKey
|
||||
files []database.File
|
||||
externalAuthLinks []database.ExternalAuthLink
|
||||
@@ -1471,6 +1472,27 @@ func (*FakeQuerier) DeleteCoordinator(context.Context, uuid.UUID) error {
|
||||
return ErrUnimplemented
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteCryptoKey(_ context.Context, arg database.DeleteCryptoKeyParams) (database.CryptoKey, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for i, key := range q.cryptoKeys {
|
||||
if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
|
||||
q.cryptoKeys[i].Secret.String = ""
|
||||
q.cryptoKeys[i].Secret.Valid = false
|
||||
q.cryptoKeys[i].SecretKeyID.String = ""
|
||||
q.cryptoKeys[i].SecretKeyID.Valid = false
|
||||
return q.cryptoKeys[i], nil
|
||||
}
|
||||
}
|
||||
return database.CryptoKey{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteCustomRole(_ context.Context, arg database.DeleteCustomRoleParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
@@ -2346,6 +2368,41 @@ func (q *FakeQuerier) GetCoordinatorResumeTokenSigningKey(_ context.Context) (st
|
||||
return q.coordinatorResumeTokenSigningKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetCryptoKeyByFeatureAndSequence(_ context.Context, arg database.GetCryptoKeyByFeatureAndSequenceParams) (database.CryptoKey, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
for _, key := range q.cryptoKeys {
|
||||
if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
|
||||
// Keys with NULL secrets are considered deleted.
|
||||
if key.Secret.Valid {
|
||||
return key, nil
|
||||
}
|
||||
return database.CryptoKey{}, sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
|
||||
return database.CryptoKey{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetCryptoKeys(_ context.Context) ([]database.CryptoKey, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
keys := make([]database.CryptoKey, 0)
|
||||
for _, key := range q.cryptoKeys {
|
||||
if key.Secret.Valid {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetDBCryptKeys(_ context.Context) ([]database.DBCryptKey, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -2849,6 +2906,22 @@ func (q *FakeQuerier) GetLastUpdateCheck(_ context.Context) (string, error) {
|
||||
return string(q.lastUpdateCheck), nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestCryptoKeyByFeature(_ context.Context, feature database.CryptoKeyFeature) (database.CryptoKey, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
var latestKey database.CryptoKey
|
||||
for _, key := range q.cryptoKeys {
|
||||
if key.Feature == feature && latestKey.Sequence < key.Sequence {
|
||||
latestKey = key
|
||||
}
|
||||
}
|
||||
if latestKey.StartsAt.IsZero() {
|
||||
return database.CryptoKey{}, sql.ErrNoRows
|
||||
}
|
||||
return latestKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -6368,6 +6441,28 @@ func (q *FakeQuerier) InsertAuditLog(_ context.Context, arg database.InsertAudit
|
||||
return alog, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertCryptoKey(_ context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
key := database.CryptoKey{
|
||||
Feature: arg.Feature,
|
||||
Sequence: arg.Sequence,
|
||||
Secret: arg.Secret,
|
||||
SecretKeyID: arg.SecretKeyID,
|
||||
StartsAt: arg.StartsAt,
|
||||
}
|
||||
|
||||
q.cryptoKeys = append(q.cryptoKeys, key)
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertCustomRole(_ context.Context, arg database.InsertCustomRoleParams) (database.CustomRole, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
@@ -7874,6 +7969,25 @@ func (q *FakeQuerier) UpdateAPIKeyByID(_ context.Context, arg database.UpdateAPI
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateCryptoKeyDeletesAt(_ context.Context, arg database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.CryptoKey{}, err
|
||||
}
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for i, key := range q.cryptoKeys {
|
||||
if key.Feature == arg.Feature && key.Sequence == arg.Sequence {
|
||||
key.DeletesAt = arg.DeletesAt
|
||||
q.cryptoKeys[i] = key
|
||||
return key, nil
|
||||
}
|
||||
}
|
||||
|
||||
return database.CryptoKey{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateCustomRole(_ context.Context, arg database.UpdateCustomRoleParams) (database.CustomRole, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
|
||||
@@ -214,6 +214,13 @@ func (m metricsStore) DeleteCoordinator(ctx context.Context, id uuid.UUID) error
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteCryptoKey(ctx context.Context, arg database.DeleteCryptoKeyParams) (database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.DeleteCryptoKey(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("DeleteCryptoKey").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteCustomRole(ctx context.Context, arg database.DeleteCustomRoleParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteCustomRole(ctx, arg)
|
||||
@@ -543,6 +550,20 @@ func (m metricsStore) GetCoordinatorResumeTokenSigningKey(ctx context.Context) (
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetCryptoKeyByFeatureAndSequence(ctx context.Context, arg database.GetCryptoKeyByFeatureAndSequenceParams) (database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetCryptoKeyByFeatureAndSequence(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("GetCryptoKeyByFeatureAndSequence").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetCryptoKeys(ctx context.Context) ([]database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetCryptoKeys(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetCryptoKeys").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetDBCryptKeys(ctx context.Context) ([]database.DBCryptKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetDBCryptKeys(ctx)
|
||||
@@ -711,6 +732,13 @@ func (m metricsStore) GetLastUpdateCheck(ctx context.Context) (string, error) {
|
||||
return version, err
|
||||
}
|
||||
|
||||
func (m metricsStore) GetLatestCryptoKeyByFeature(ctx context.Context, feature database.CryptoKeyFeature) (database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetLatestCryptoKeyByFeature(ctx, feature)
|
||||
m.queryLatencies.WithLabelValues("GetLatestCryptoKeyByFeature").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
start := time.Now()
|
||||
build, err := m.s.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspaceID)
|
||||
@@ -1600,6 +1628,13 @@ func (m metricsStore) InsertAuditLog(ctx context.Context, arg database.InsertAud
|
||||
return log, err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertCryptoKey(ctx context.Context, arg database.InsertCryptoKeyParams) (database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
key, err := m.s.InsertCryptoKey(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertCryptoKey").Observe(time.Since(start).Seconds())
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertCustomRole(ctx context.Context, arg database.InsertCustomRoleParams) (database.CustomRole, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertCustomRole(ctx, arg)
|
||||
@@ -2006,6 +2041,13 @@ func (m metricsStore) UpdateAPIKeyByID(ctx context.Context, arg database.UpdateA
|
||||
return err
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateCryptoKeyDeletesAt(ctx context.Context, arg database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {
|
||||
start := time.Now()
|
||||
key, err := m.s.UpdateCryptoKeyDeletesAt(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateCryptoKeyDeletesAt").Observe(time.Since(start).Seconds())
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateCustomRole(ctx context.Context, arg database.UpdateCustomRoleParams) (database.CustomRole, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.UpdateCustomRole(ctx, arg)
|
||||
|
||||
@@ -317,6 +317,21 @@ func (mr *MockStoreMockRecorder) DeleteCoordinator(arg0, arg1 any) *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCoordinator", reflect.TypeOf((*MockStore)(nil).DeleteCoordinator), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteCryptoKey mocks base method.
|
||||
func (m *MockStore) DeleteCryptoKey(arg0 context.Context, arg1 database.DeleteCryptoKeyParams) (database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteCryptoKey", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// DeleteCryptoKey indicates an expected call of DeleteCryptoKey.
|
||||
func (mr *MockStoreMockRecorder) DeleteCryptoKey(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCryptoKey", reflect.TypeOf((*MockStore)(nil).DeleteCryptoKey), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteCustomRole mocks base method.
|
||||
func (m *MockStore) DeleteCustomRole(arg0 context.Context, arg1 database.DeleteCustomRoleParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1058,6 +1073,36 @@ func (mr *MockStoreMockRecorder) GetCoordinatorResumeTokenSigningKey(arg0 any) *
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCoordinatorResumeTokenSigningKey", reflect.TypeOf((*MockStore)(nil).GetCoordinatorResumeTokenSigningKey), arg0)
|
||||
}
|
||||
|
||||
// GetCryptoKeyByFeatureAndSequence mocks base method.
|
||||
func (m *MockStore) GetCryptoKeyByFeatureAndSequence(arg0 context.Context, arg1 database.GetCryptoKeyByFeatureAndSequenceParams) (database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCryptoKeyByFeatureAndSequence", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetCryptoKeyByFeatureAndSequence indicates an expected call of GetCryptoKeyByFeatureAndSequence.
|
||||
func (mr *MockStoreMockRecorder) GetCryptoKeyByFeatureAndSequence(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCryptoKeyByFeatureAndSequence", reflect.TypeOf((*MockStore)(nil).GetCryptoKeyByFeatureAndSequence), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetCryptoKeys mocks base method.
|
||||
func (m *MockStore) GetCryptoKeys(arg0 context.Context) ([]database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetCryptoKeys", arg0)
|
||||
ret0, _ := ret[0].([]database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetCryptoKeys indicates an expected call of GetCryptoKeys.
|
||||
func (mr *MockStoreMockRecorder) GetCryptoKeys(arg0 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCryptoKeys", reflect.TypeOf((*MockStore)(nil).GetCryptoKeys), arg0)
|
||||
}
|
||||
|
||||
// GetDBCryptKeys mocks base method.
|
||||
func (m *MockStore) GetDBCryptKeys(arg0 context.Context) ([]database.DBCryptKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1418,6 +1463,21 @@ func (mr *MockStoreMockRecorder) GetLastUpdateCheck(arg0 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLastUpdateCheck", reflect.TypeOf((*MockStore)(nil).GetLastUpdateCheck), arg0)
|
||||
}
|
||||
|
||||
// GetLatestCryptoKeyByFeature mocks base method.
|
||||
func (m *MockStore) GetLatestCryptoKeyByFeature(arg0 context.Context, arg1 database.CryptoKeyFeature) (database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetLatestCryptoKeyByFeature", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetLatestCryptoKeyByFeature indicates an expected call of GetLatestCryptoKeyByFeature.
|
||||
func (mr *MockStoreMockRecorder) GetLatestCryptoKeyByFeature(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestCryptoKeyByFeature", reflect.TypeOf((*MockStore)(nil).GetLatestCryptoKeyByFeature), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetLatestWorkspaceBuildByWorkspaceID mocks base method.
|
||||
func (m *MockStore) GetLatestWorkspaceBuildByWorkspaceID(arg0 context.Context, arg1 uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -3367,6 +3427,21 @@ func (mr *MockStoreMockRecorder) InsertAuditLog(arg0, arg1 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertAuditLog", reflect.TypeOf((*MockStore)(nil).InsertAuditLog), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertCryptoKey mocks base method.
|
||||
func (m *MockStore) InsertCryptoKey(arg0 context.Context, arg1 database.InsertCryptoKeyParams) (database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertCryptoKey", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertCryptoKey indicates an expected call of InsertCryptoKey.
|
||||
func (mr *MockStoreMockRecorder) InsertCryptoKey(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertCryptoKey", reflect.TypeOf((*MockStore)(nil).InsertCryptoKey), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertCustomRole mocks base method.
|
||||
func (m *MockStore) InsertCustomRole(arg0 context.Context, arg1 database.InsertCustomRoleParams) (database.CustomRole, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -4234,6 +4309,21 @@ func (mr *MockStoreMockRecorder) UpdateAPIKeyByID(arg0, arg1 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAPIKeyByID", reflect.TypeOf((*MockStore)(nil).UpdateAPIKeyByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateCryptoKeyDeletesAt mocks base method.
|
||||
func (m *MockStore) UpdateCryptoKeyDeletesAt(arg0 context.Context, arg1 database.UpdateCryptoKeyDeletesAtParams) (database.CryptoKey, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateCryptoKeyDeletesAt", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.CryptoKey)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UpdateCryptoKeyDeletesAt indicates an expected call of UpdateCryptoKeyDeletesAt.
|
||||
func (mr *MockStoreMockRecorder) UpdateCryptoKeyDeletesAt(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateCryptoKeyDeletesAt", reflect.TypeOf((*MockStore)(nil).UpdateCryptoKeyDeletesAt), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateCustomRole mocks base method.
|
||||
func (m *MockStore) UpdateCustomRole(arg0 context.Context, arg1 database.UpdateCustomRoleParams) (database.CustomRole, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+21
@@ -36,6 +36,12 @@ CREATE TYPE build_reason AS ENUM (
|
||||
'autodelete'
|
||||
);
|
||||
|
||||
CREATE TYPE crypto_key_feature AS ENUM (
|
||||
'workspace_apps',
|
||||
'oidc_convert',
|
||||
'tailnet_resume'
|
||||
);
|
||||
|
||||
CREATE TYPE display_app AS ENUM (
|
||||
'vscode',
|
||||
'vscode_insiders',
|
||||
@@ -494,6 +500,15 @@ CREATE TABLE audit_logs (
|
||||
resource_icon text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE crypto_keys (
|
||||
feature crypto_key_feature NOT NULL,
|
||||
sequence integer NOT NULL,
|
||||
secret text,
|
||||
secret_key_id text,
|
||||
starts_at timestamp with time zone NOT NULL,
|
||||
deletes_at timestamp with time zone
|
||||
);
|
||||
|
||||
CREATE TABLE custom_roles (
|
||||
name text NOT NULL,
|
||||
display_name text NOT NULL,
|
||||
@@ -1641,6 +1656,9 @@ ALTER TABLE ONLY api_keys
|
||||
ALTER TABLE ONLY audit_logs
|
||||
ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY crypto_keys
|
||||
ADD CONSTRAINT crypto_keys_pkey PRIMARY KEY (feature, sequence);
|
||||
|
||||
ALTER TABLE ONLY custom_roles
|
||||
ADD CONSTRAINT custom_roles_unique_key UNIQUE (name, organization_id);
|
||||
|
||||
@@ -2036,6 +2054,9 @@ CREATE TRIGGER update_notification_message_dedupe_hash BEFORE INSERT OR UPDATE O
|
||||
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 crypto_keys
|
||||
ADD CONSTRAINT crypto_keys_secret_key_id_fkey FOREIGN KEY (secret_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
|
||||
ALTER TABLE ONLY external_auth_links
|
||||
ADD CONSTRAINT git_auth_links_oauth_access_token_key_id_fkey FOREIGN KEY (oauth_access_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ type ForeignKeyConstraint string
|
||||
// ForeignKeyConstraint enums.
|
||||
const (
|
||||
ForeignKeyAPIKeysUserIDUUID ForeignKeyConstraint = "api_keys_user_id_uuid_fkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyCryptoKeysSecretKeyID ForeignKeyConstraint = "crypto_keys_secret_key_id_fkey" // ALTER TABLE ONLY crypto_keys ADD CONSTRAINT crypto_keys_secret_key_id_fkey FOREIGN KEY (secret_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
ForeignKeyGitAuthLinksOauthAccessTokenKeyID ForeignKeyConstraint = "git_auth_links_oauth_access_token_key_id_fkey" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_oauth_access_token_key_id_fkey FOREIGN KEY (oauth_access_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
ForeignKeyGitAuthLinksOauthRefreshTokenKeyID ForeignKeyConstraint = "git_auth_links_oauth_refresh_token_key_id_fkey" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_oauth_refresh_token_key_id_fkey FOREIGN KEY (oauth_refresh_token_key_id) REFERENCES dbcrypt_keys(active_key_digest);
|
||||
ForeignKeyGitSSHKeysUserID ForeignKeyConstraint = "gitsshkeys_user_id_fkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
DROP TABLE "crypto_keys";
|
||||
DROP TYPE "crypto_key_feature";
|
||||
@@ -0,0 +1,16 @@
|
||||
CREATE TYPE crypto_key_feature AS ENUM (
|
||||
'workspace_apps',
|
||||
'oidc_convert',
|
||||
'tailnet_resume'
|
||||
);
|
||||
|
||||
CREATE TABLE crypto_keys (
|
||||
feature crypto_key_feature NOT NULL,
|
||||
sequence integer NOT NULL,
|
||||
secret text NULL,
|
||||
secret_key_id text NULL REFERENCES dbcrypt_keys(active_key_digest),
|
||||
starts_at timestamptz NOT NULL,
|
||||
deletes_at timestamptz NULL,
|
||||
PRIMARY KEY (feature, sequence)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
INSERT INTO crypto_keys (feature, sequence, starts_at, secret) VALUES
|
||||
('workspace_apps', 1, now(), 'abc'),
|
||||
('oidc_convert', 1, now(), 'def'),
|
||||
('tailnet_resume', 1, now(), 'ghi');
|
||||
@@ -447,3 +447,7 @@ func (r GetAuthorizationUserRolesRow) RoleNames() ([]rbac.RoleIdentifier, error)
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (k CryptoKey) ExpiresAt(keyDuration time.Duration) time.Time {
|
||||
return k.StartsAt.Add(keyDuration).UTC()
|
||||
}
|
||||
|
||||
@@ -339,6 +339,67 @@ func AllBuildReasonValues() []BuildReason {
|
||||
}
|
||||
}
|
||||
|
||||
type CryptoKeyFeature string
|
||||
|
||||
const (
|
||||
CryptoKeyFeatureWorkspaceApps CryptoKeyFeature = "workspace_apps"
|
||||
CryptoKeyFeatureOidcConvert CryptoKeyFeature = "oidc_convert"
|
||||
CryptoKeyFeatureTailnetResume CryptoKeyFeature = "tailnet_resume"
|
||||
)
|
||||
|
||||
func (e *CryptoKeyFeature) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = CryptoKeyFeature(s)
|
||||
case string:
|
||||
*e = CryptoKeyFeature(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for CryptoKeyFeature: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullCryptoKeyFeature struct {
|
||||
CryptoKeyFeature CryptoKeyFeature `json:"crypto_key_feature"`
|
||||
Valid bool `json:"valid"` // Valid is true if CryptoKeyFeature is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullCryptoKeyFeature) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.CryptoKeyFeature, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.CryptoKeyFeature.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullCryptoKeyFeature) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.CryptoKeyFeature), nil
|
||||
}
|
||||
|
||||
func (e CryptoKeyFeature) Valid() bool {
|
||||
switch e {
|
||||
case CryptoKeyFeatureWorkspaceApps,
|
||||
CryptoKeyFeatureOidcConvert,
|
||||
CryptoKeyFeatureTailnetResume:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllCryptoKeyFeatureValues() []CryptoKeyFeature {
|
||||
return []CryptoKeyFeature{
|
||||
CryptoKeyFeatureWorkspaceApps,
|
||||
CryptoKeyFeatureOidcConvert,
|
||||
CryptoKeyFeatureTailnetResume,
|
||||
}
|
||||
}
|
||||
|
||||
type DisplayApp string
|
||||
|
||||
const (
|
||||
@@ -2043,6 +2104,15 @@ type AuditLog struct {
|
||||
ResourceIcon string `db:"resource_icon" json:"resource_icon"`
|
||||
}
|
||||
|
||||
type CryptoKey struct {
|
||||
Feature CryptoKeyFeature `db:"feature" json:"feature"`
|
||||
Sequence int32 `db:"sequence" json:"sequence"`
|
||||
Secret sql.NullString `db:"secret" json:"secret"`
|
||||
SecretKeyID sql.NullString `db:"secret_key_id" json:"secret_key_id"`
|
||||
StartsAt time.Time `db:"starts_at" json:"starts_at"`
|
||||
DeletesAt sql.NullTime `db:"deletes_at" json:"deletes_at"`
|
||||
}
|
||||
|
||||
// Custom roles allow dynamic roles expanded at runtime
|
||||
type CustomRole struct {
|
||||
Name string `db:"name" json:"name"`
|
||||
|
||||
@@ -69,6 +69,7 @@ type sqlcQuerier interface {
|
||||
DeleteAllTailnetTunnels(ctx context.Context, arg DeleteAllTailnetTunnelsParams) error
|
||||
DeleteApplicationConnectAPIKeysByUserID(ctx context.Context, userID uuid.UUID) error
|
||||
DeleteCoordinator(ctx context.Context, id uuid.UUID) error
|
||||
DeleteCryptoKey(ctx context.Context, arg DeleteCryptoKeyParams) (CryptoKey, error)
|
||||
DeleteCustomRole(ctx context.Context, arg DeleteCustomRoleParams) error
|
||||
DeleteExternalAuthLink(ctx context.Context, arg DeleteExternalAuthLinkParams) error
|
||||
DeleteGitSSHKey(ctx context.Context, userID uuid.UUID) error
|
||||
@@ -131,6 +132,8 @@ type sqlcQuerier interface {
|
||||
// are included.
|
||||
GetAuthorizationUserRoles(ctx context.Context, userID uuid.UUID) (GetAuthorizationUserRolesRow, error)
|
||||
GetCoordinatorResumeTokenSigningKey(ctx context.Context) (string, error)
|
||||
GetCryptoKeyByFeatureAndSequence(ctx context.Context, arg GetCryptoKeyByFeatureAndSequenceParams) (CryptoKey, error)
|
||||
GetCryptoKeys(ctx context.Context) ([]CryptoKey, error)
|
||||
GetDBCryptKeys(ctx context.Context) ([]DBCryptKey, error)
|
||||
GetDERPMeshKey(ctx context.Context) (string, error)
|
||||
GetDefaultOrganization(ctx context.Context) (Organization, error)
|
||||
@@ -159,6 +162,7 @@ type sqlcQuerier interface {
|
||||
GetHungProvisionerJobs(ctx context.Context, updatedAt time.Time) ([]ProvisionerJob, error)
|
||||
GetJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg GetJFrogXrayScanByWorkspaceAndAgentIDParams) (JfrogXrayScan, error)
|
||||
GetLastUpdateCheck(ctx context.Context) (string, error)
|
||||
GetLatestCryptoKeyByFeature(ctx context.Context, feature CryptoKeyFeature) (CryptoKey, error)
|
||||
GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error)
|
||||
GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error)
|
||||
GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error)
|
||||
@@ -338,6 +342,7 @@ type sqlcQuerier interface {
|
||||
// every member of the org.
|
||||
InsertAllUsersGroup(ctx context.Context, organizationID uuid.UUID) (Group, error)
|
||||
InsertAuditLog(ctx context.Context, arg InsertAuditLogParams) (AuditLog, error)
|
||||
InsertCryptoKey(ctx context.Context, arg InsertCryptoKeyParams) (CryptoKey, error)
|
||||
InsertCustomRole(ctx context.Context, arg InsertCustomRoleParams) (CustomRole, error)
|
||||
InsertDBCryptKey(ctx context.Context, arg InsertDBCryptKeyParams) error
|
||||
InsertDERPMeshKey(ctx context.Context, value string) error
|
||||
@@ -412,6 +417,7 @@ type sqlcQuerier interface {
|
||||
UnarchiveTemplateVersion(ctx context.Context, arg UnarchiveTemplateVersionParams) error
|
||||
UnfavoriteWorkspace(ctx context.Context, id uuid.UUID) error
|
||||
UpdateAPIKeyByID(ctx context.Context, arg UpdateAPIKeyByIDParams) error
|
||||
UpdateCryptoKeyDeletesAt(ctx context.Context, arg UpdateCryptoKeyDeletesAtParams) (CryptoKey, error)
|
||||
UpdateCustomRole(ctx context.Context, arg UpdateCustomRoleParams) (CustomRole, error)
|
||||
UpdateExternalAuthLink(ctx context.Context, arg UpdateExternalAuthLinkParams) (ExternalAuthLink, error)
|
||||
UpdateGitSSHKey(ctx context.Context, arg UpdateGitSSHKeyParams) (GitSSHKey, error)
|
||||
|
||||
@@ -761,6 +761,186 @@ func (q *sqlQuerier) InsertAuditLog(ctx context.Context, arg InsertAuditLogParam
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteCryptoKey = `-- name: DeleteCryptoKey :one
|
||||
UPDATE crypto_keys
|
||||
SET secret = NULL, secret_key_id = NULL
|
||||
WHERE feature = $1 AND sequence = $2 RETURNING feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
`
|
||||
|
||||
type DeleteCryptoKeyParams struct {
|
||||
Feature CryptoKeyFeature `db:"feature" json:"feature"`
|
||||
Sequence int32 `db:"sequence" json:"sequence"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) DeleteCryptoKey(ctx context.Context, arg DeleteCryptoKeyParams) (CryptoKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, deleteCryptoKey, arg.Feature, arg.Sequence)
|
||||
var i CryptoKey
|
||||
err := row.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCryptoKeyByFeatureAndSequence = `-- name: GetCryptoKeyByFeatureAndSequence :one
|
||||
SELECT feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
FROM crypto_keys
|
||||
WHERE feature = $1
|
||||
AND sequence = $2
|
||||
AND secret IS NOT NULL
|
||||
`
|
||||
|
||||
type GetCryptoKeyByFeatureAndSequenceParams struct {
|
||||
Feature CryptoKeyFeature `db:"feature" json:"feature"`
|
||||
Sequence int32 `db:"sequence" json:"sequence"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetCryptoKeyByFeatureAndSequence(ctx context.Context, arg GetCryptoKeyByFeatureAndSequenceParams) (CryptoKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCryptoKeyByFeatureAndSequence, arg.Feature, arg.Sequence)
|
||||
var i CryptoKey
|
||||
err := row.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCryptoKeys = `-- name: GetCryptoKeys :many
|
||||
SELECT feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
FROM crypto_keys
|
||||
WHERE secret IS NOT NULL
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetCryptoKeys(ctx context.Context) ([]CryptoKey, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCryptoKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []CryptoKey
|
||||
for rows.Next() {
|
||||
var i CryptoKey
|
||||
if err := rows.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
); 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 getLatestCryptoKeyByFeature = `-- name: GetLatestCryptoKeyByFeature :one
|
||||
SELECT feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
FROM crypto_keys
|
||||
WHERE feature = $1
|
||||
ORDER BY sequence DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetLatestCryptoKeyByFeature(ctx context.Context, feature CryptoKeyFeature) (CryptoKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, getLatestCryptoKeyByFeature, feature)
|
||||
var i CryptoKey
|
||||
err := row.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertCryptoKey = `-- name: InsertCryptoKey :one
|
||||
INSERT INTO crypto_keys (
|
||||
feature,
|
||||
sequence,
|
||||
secret,
|
||||
starts_at,
|
||||
secret_key_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
) RETURNING feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
`
|
||||
|
||||
type InsertCryptoKeyParams struct {
|
||||
Feature CryptoKeyFeature `db:"feature" json:"feature"`
|
||||
Sequence int32 `db:"sequence" json:"sequence"`
|
||||
Secret sql.NullString `db:"secret" json:"secret"`
|
||||
StartsAt time.Time `db:"starts_at" json:"starts_at"`
|
||||
SecretKeyID sql.NullString `db:"secret_key_id" json:"secret_key_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertCryptoKey(ctx context.Context, arg InsertCryptoKeyParams) (CryptoKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertCryptoKey,
|
||||
arg.Feature,
|
||||
arg.Sequence,
|
||||
arg.Secret,
|
||||
arg.StartsAt,
|
||||
arg.SecretKeyID,
|
||||
)
|
||||
var i CryptoKey
|
||||
err := row.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateCryptoKeyDeletesAt = `-- name: UpdateCryptoKeyDeletesAt :one
|
||||
UPDATE crypto_keys
|
||||
SET deletes_at = $3
|
||||
WHERE feature = $1 AND sequence = $2 RETURNING feature, sequence, secret, secret_key_id, starts_at, deletes_at
|
||||
`
|
||||
|
||||
type UpdateCryptoKeyDeletesAtParams struct {
|
||||
Feature CryptoKeyFeature `db:"feature" json:"feature"`
|
||||
Sequence int32 `db:"sequence" json:"sequence"`
|
||||
DeletesAt sql.NullTime `db:"deletes_at" json:"deletes_at"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateCryptoKeyDeletesAt(ctx context.Context, arg UpdateCryptoKeyDeletesAtParams) (CryptoKey, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateCryptoKeyDeletesAt, arg.Feature, arg.Sequence, arg.DeletesAt)
|
||||
var i CryptoKey
|
||||
err := row.Scan(
|
||||
&i.Feature,
|
||||
&i.Sequence,
|
||||
&i.Secret,
|
||||
&i.SecretKeyID,
|
||||
&i.StartsAt,
|
||||
&i.DeletesAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getDBCryptKeys = `-- name: GetDBCryptKeys :many
|
||||
SELECT number, active_key_digest, revoked_key_digest, created_at, revoked_at, test FROM dbcrypt_keys ORDER BY number ASC
|
||||
`
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
-- name: GetCryptoKeys :many
|
||||
SELECT *
|
||||
FROM crypto_keys
|
||||
WHERE secret IS NOT NULL;
|
||||
|
||||
-- name: GetLatestCryptoKeyByFeature :one
|
||||
SELECT *
|
||||
FROM crypto_keys
|
||||
WHERE feature = $1
|
||||
ORDER BY sequence DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetCryptoKeyByFeatureAndSequence :one
|
||||
SELECT *
|
||||
FROM crypto_keys
|
||||
WHERE feature = $1
|
||||
AND sequence = $2
|
||||
AND secret IS NOT NULL;
|
||||
|
||||
-- name: DeleteCryptoKey :one
|
||||
UPDATE crypto_keys
|
||||
SET secret = NULL, secret_key_id = NULL
|
||||
WHERE feature = $1 AND sequence = $2 RETURNING *;
|
||||
|
||||
-- name: InsertCryptoKey :one
|
||||
INSERT INTO crypto_keys (
|
||||
feature,
|
||||
sequence,
|
||||
secret,
|
||||
starts_at,
|
||||
secret_key_id
|
||||
) VALUES (
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UpdateCryptoKeyDeletesAt :one
|
||||
UPDATE crypto_keys
|
||||
SET deletes_at = $3
|
||||
WHERE feature = $1 AND sequence = $2 RETURNING *;
|
||||
@@ -9,6 +9,7 @@ const (
|
||||
UniqueAgentStatsPkey UniqueConstraint = "agent_stats_pkey" // ALTER TABLE ONLY workspace_agent_stats ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id);
|
||||
UniqueAPIKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id);
|
||||
UniqueAuditLogsPkey UniqueConstraint = "audit_logs_pkey" // ALTER TABLE ONLY audit_logs ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id);
|
||||
UniqueCryptoKeysPkey UniqueConstraint = "crypto_keys_pkey" // ALTER TABLE ONLY crypto_keys ADD CONSTRAINT crypto_keys_pkey PRIMARY KEY (feature, sequence);
|
||||
UniqueCustomRolesUniqueKey UniqueConstraint = "custom_roles_unique_key" // ALTER TABLE ONLY custom_roles ADD CONSTRAINT custom_roles_unique_key UNIQUE (name, organization_id);
|
||||
UniqueDbcryptKeysActiveKeyDigestKey UniqueConstraint = "dbcrypt_keys_active_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_active_key_digest_key UNIQUE (active_key_digest);
|
||||
UniqueDbcryptKeysPkey UniqueConstraint = "dbcrypt_keys_pkey" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_pkey PRIMARY KEY (number);
|
||||
|
||||
Reference in New Issue
Block a user