From d54e56af5e23c9abd2354d31201903c8fe7a44cb Mon Sep 17 00:00:00 2001 From: Asher Date: Mon, 8 Jan 2024 15:19:48 -0900 Subject: [PATCH 1/8] Add OAuth2 provider codes and tokens to database --- coderd/database/dbauthz/dbauthz.go | 67 +++++ coderd/database/dbauthz/dbauthz_test.go | 130 +++++++++ coderd/database/dbgen/dbgen.go | 26 ++ coderd/database/dbmem/dbmem.go | 263 +++++++++++++++++- coderd/database/dbmetrics/dbmetrics.go | 63 +++++ coderd/database/dbmock/dbmock.go | 132 +++++++++ coderd/database/dump.sql | 62 ++++- coderd/database/foreign_key_constraint.go | 4 + .../000186_oauth2_provider_codes.down.sql | 8 + .../000186_oauth2_provider_codes.up.sql | 44 +++ .../000186_oauth2_provider_codes.up.sql | 21 ++ coderd/database/modelmethods.go | 16 ++ coderd/database/models.go | 35 ++- coderd/database/querier.go | 9 + coderd/database/queries.sql.go | 253 +++++++++++++++++ coderd/database/queries/oauth2.sql | 71 +++++ coderd/database/sqlc.yaml | 4 + coderd/database/unique_constraint.go | 146 +++++----- coderd/rbac/object.go | 12 +- coderd/rbac/object_gen.go | 1 + coderd/rbac/roles.go | 2 + 21 files changed, 1287 insertions(+), 82 deletions(-) create mode 100644 coderd/database/migrations/000186_oauth2_provider_codes.down.sql create mode 100644 coderd/database/migrations/000186_oauth2_provider_codes.up.sql create mode 100644 coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index a5b295e2e35eb..a9abce9712ae5 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -821,6 +821,25 @@ func (q *querier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) return q.db.DeleteOAuth2ProviderAppByID(ctx, id) } +func (q *querier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error { + code, err := q.db.GetOAuth2ProviderAppCodeByID(ctx, id) + if err != nil { + return err + } + if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(code.UserID.String())); err != nil { + return err + } + return q.db.DeleteOAuth2ProviderAppCodeByID(ctx, id) +} + +func (q *querier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error { + if err := q.authorizeContext(ctx, rbac.ActionDelete, + rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil { + return err + } + return q.db.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, arg) +} + func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error { if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceOAuth2ProviderAppSecret); err != nil { return err @@ -828,6 +847,14 @@ func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid return q.db.DeleteOAuth2ProviderAppSecretByID(ctx, id) } +func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error { + if err := q.authorizeContext(ctx, rbac.ActionDelete, + rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil { + return err + } + return q.db.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg) +} + func (q *querier) DeleteOldProvisionerDaemons(ctx context.Context) error { if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil { return err @@ -1161,6 +1188,18 @@ func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (d return q.db.GetOAuth2ProviderAppByID(ctx, id) } +func (q *querier) GetOAuth2ProviderAppCodeByAppIDAndSecret(ctx context.Context, arg database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (database.OAuth2ProviderAppCode, error) { + return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByAppIDAndSecret)(ctx, arg) +} + +func (q *querier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) { + return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByID)(ctx, id) +} + +func (q *querier) GetOAuth2ProviderAppSecretByAppIDAndSecret(ctx context.Context, arg database.GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (database.OAuth2ProviderAppSecret, error) { + return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppSecretByAppIDAndSecret)(ctx, arg) +} + func (q *querier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) { if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppSecret); err != nil { return database.OAuth2ProviderAppSecret{}, err @@ -1182,6 +1221,15 @@ func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2P return q.db.GetOAuth2ProviderApps(ctx) } +func (q *querier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) { + // These two authz checks make sure the caller can read all their own tokens. + if err := q.authorizeContext(ctx, rbac.ActionRead, + rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(userID.String())); err != nil { + return []database.GetOAuth2ProviderAppsByUserIDRow{}, err + } + return q.db.GetOAuth2ProviderAppsByUserID(ctx, userID) +} + func (q *querier) GetOAuthSigningKey(ctx context.Context) (string, error) { if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil { return "", err @@ -2203,6 +2251,14 @@ func (q *querier) InsertOAuth2ProviderApp(ctx context.Context, arg database.Inse return q.db.InsertOAuth2ProviderApp(ctx, arg) } +func (q *querier) InsertOAuth2ProviderAppCode(ctx context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) { + if err := q.authorizeContext(ctx, rbac.ActionCreate, + rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil { + return database.OAuth2ProviderAppCode{}, err + } + return q.db.InsertOAuth2ProviderAppCode(ctx, arg) +} + func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) { if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppSecret); err != nil { return database.OAuth2ProviderAppSecret{}, err @@ -2210,6 +2266,17 @@ func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg databas return q.db.InsertOAuth2ProviderAppSecret(ctx, arg) } +func (q *querier) InsertOAuth2ProviderAppToken(ctx context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) { + key, err := q.db.GetAPIKeyByID(ctx, arg.APIKeyID) + if err != nil { + return database.OAuth2ProviderAppToken{}, err + } + if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(key.UserID.String())); err != nil { + return database.OAuth2ProviderAppToken{}, err + } + return q.db.InsertOAuth2ProviderAppToken(ctx, arg) +} + func (q *querier) InsertOrganization(ctx context.Context, arg database.InsertOrganizationParams) (database.Organization, error) { return insert(q.log, q.auth, rbac.ResourceOrganization, q.db.InsertOrganization)(ctx, arg) } diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index d9444278722e7..79a22081b0bae 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2220,6 +2220,34 @@ func (s *MethodTestSuite) TestOAuth2ProviderApps() { app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) check.Args(app.ID).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionRead).Returns(app) })) + s.Run("GetOAuth2ProviderAppsByUserID", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + key, _ := dbgen.APIKey(s.T(), db, database.APIKey{ + UserID: user.ID, + }) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + _ = dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{ + AppID: app.ID, + }) + for i := 0; i < 5; i++ { + _ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{ + AppSecretID: secret.ID, + APIKeyID: key.ID, + }) + } + check.Args(user.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns([]database.GetOAuth2ProviderAppsByUserIDRow{ + { + OAuth2ProviderApp: database.OAuth2ProviderApp{ + ID: app.ID, + CallbackURL: app.CallbackURL, + Icon: app.Icon, + Name: app.Name, + }, + TokenCount: 5, + }, + }) + })) s.Run("InsertOAuth2ProviderApp", s.Subtest(func(db database.Store, check *expects) { check.Args(database.InsertOAuth2ProviderAppParams{}).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionCreate) })) @@ -2265,6 +2293,16 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() { }) check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret) })) + s.Run("GetOAuth2ProviderAppSecretByAppIDAndSecret", s.Subtest(func(db database.Store, check *expects) { + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{ + AppID: app.ID, + }) + check.Args(database.GetOAuth2ProviderAppSecretByAppIDAndSecretParams{ + AppID: app.ID, + HashedSecret: secret.HashedSecret, + }).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret) + })) s.Run("InsertOAuth2ProviderAppSecret", s.Subtest(func(db database.Store, check *expects) { app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) check.Args(database.InsertOAuth2ProviderAppSecretParams{ @@ -2290,3 +2328,95 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() { check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionDelete) })) } + +func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() { + s.Run("GetOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{ + AppID: app.ID, + UserID: user.ID, + }) + check.Args(code.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns(code) + })) + s.Run("GetOAuth2ProviderAppCodeByAppIDAndSecret", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{ + AppID: app.ID, + UserID: user.ID, + }) + check.Args(database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams{ + AppID: app.ID, + HashedSecret: code.HashedSecret, + }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns(code) + })) + s.Run("InsertOAuth2ProviderAppCode", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + check.Args(database.InsertOAuth2ProviderAppCodeParams{ + AppID: app.ID, + UserID: user.ID, + }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate) + })) + s.Run("DeleteOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{ + AppID: app.ID, + UserID: user.ID, + }) + check.Args(code.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete) + })) + s.Run("DeleteOAuth2ProviderAppCodesByAppAndUserID", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + for i := 0; i < 5; i++ { + _ = dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{ + AppID: app.ID, + UserID: user.ID, + }) + } + check.Args(database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams{ + AppID: app.ID, + UserID: user.ID, + }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete) + })) +} + +func (s *MethodTestSuite) TestOAuth2ProviderAppTokens() { + s.Run("InsertOAuth2ProviderAppToken", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + key, _ := dbgen.APIKey(s.T(), db, database.APIKey{ + UserID: user.ID, + }) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{ + AppID: app.ID, + }) + check.Args(database.InsertOAuth2ProviderAppTokenParams{ + AppSecretID: secret.ID, + APIKeyID: key.ID, + }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate) + })) + s.Run("DeleteOAuth2ProviderAppTokensByAppAndUserID", s.Subtest(func(db database.Store, check *expects) { + user := dbgen.User(s.T(), db, database.User{}) + key, _ := dbgen.APIKey(s.T(), db, database.APIKey{ + UserID: user.ID, + }) + app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{}) + secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{ + AppID: app.ID, + }) + for i := 0; i < 5; i++ { + _ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{ + AppSecretID: secret.ID, + APIKeyID: key.ID, + }) + } + check.Args(database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams{ + AppID: app.ID, + UserID: user.ID, + }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete) + })) +} diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index a4101151d2858..accc593712658 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -704,6 +704,32 @@ func OAuth2ProviderAppSecret(t testing.TB, db database.Store, seed database.OAut return app } +func OAuth2ProviderAppCode(t testing.TB, db database.Store, seed database.OAuth2ProviderAppCode) database.OAuth2ProviderAppCode { + code, err := db.InsertOAuth2ProviderAppCode(genCtx, database.InsertOAuth2ProviderAppCodeParams{ + ID: takeFirst(seed.ID, uuid.New()), + CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), + ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()), + HashedSecret: takeFirstSlice(seed.HashedSecret, []byte("hashed-secret")), + AppID: takeFirst(seed.AppID, uuid.New()), + UserID: takeFirst(seed.UserID, uuid.New()), + }) + require.NoError(t, err, "insert oauth2 app code") + return code +} + +func OAuth2ProviderAppToken(t testing.TB, db database.Store, seed database.OAuth2ProviderAppToken) database.OAuth2ProviderAppToken { + token, err := db.InsertOAuth2ProviderAppToken(genCtx, database.InsertOAuth2ProviderAppTokenParams{ + ID: takeFirst(seed.ID, uuid.New()), + CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), + ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()), + HashedSecret: takeFirstSlice(seed.HashedSecret, []byte("hashed-secret")), + AppSecretID: takeFirst(seed.AppSecretID, uuid.New()), + APIKeyID: takeFirst(seed.APIKeyID, uuid.New().String()), + }) + require.NoError(t, err, "insert oauth2 app token") + return token +} + func must[V any](v V, err error) V { if err != nil { panic(err) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index e3572a2e10ff1..95287b50bd48a 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -1,6 +1,7 @@ package dbmem import ( + "bytes" "context" "database/sql" "encoding/json" @@ -132,6 +133,8 @@ type data struct { licenses []database.License oauth2ProviderApps []database.OAuth2ProviderApp oauth2ProviderAppSecrets []database.OAuth2ProviderAppSecret + oauth2ProviderAppCodes []database.OAuth2ProviderAppCode + oauth2ProviderAppTokens []database.OAuth2ProviderAppToken parameterSchemas []database.ParameterSchema provisionerDaemons []database.ProvisionerDaemon provisionerJobLogs []database.ProvisionerJobLog @@ -1180,14 +1183,72 @@ func (q *FakeQuerier) DeleteOAuth2ProviderAppByID(_ context.Context, id uuid.UUI q.oauth2ProviderApps[index] = q.oauth2ProviderApps[len(q.oauth2ProviderApps)-1] q.oauth2ProviderApps = q.oauth2ProviderApps[:len(q.oauth2ProviderApps)-1] - secrets := []database.OAuth2ProviderAppSecret{} + // Cascade delete into secrets. + var deletedSecretIDs []uuid.UUID + var secrets []database.OAuth2ProviderAppSecret for _, secret := range q.oauth2ProviderAppSecrets { - if secret.AppID != id { + if secret.AppID == id { + deletedSecretIDs = append(deletedSecretIDs, secret.ID) + } else { secrets = append(secrets, secret) } } q.oauth2ProviderAppSecrets = secrets + // Cascade delete into tokens. + var keyIDsToDelete []string + var tokens []database.OAuth2ProviderAppToken + for _, token := range q.oauth2ProviderAppTokens { + if slice.Contains(deletedSecretIDs, token.AppSecretID) { + keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID) + } else { + tokens = append(tokens, token) + } + } + q.oauth2ProviderAppTokens = tokens + + // Delete from API keys. + var keys []database.APIKey + for _, key := range q.apiKeys { + if !slices.Contains(keyIDsToDelete, key.ID) { + keys = append(keys, key) + } + } + q.apiKeys = keys + + return nil + } + } + return sql.ErrNoRows +} + +func (q *FakeQuerier) DeleteOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) error { + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, code := range q.oauth2ProviderAppCodes { + if code.ID == id { + q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1] + q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1] + return nil + } + } + return sql.ErrNoRows +} + +func (q *FakeQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error { + err := validateDatabaseType(arg) + if err != nil { + return err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for index, code := range q.oauth2ProviderAppCodes { + if code.AppID == arg.AppID && code.UserID == arg.UserID { + q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1] + q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1] return nil } } @@ -1202,12 +1263,73 @@ func (q *FakeQuerier) DeleteOAuth2ProviderAppSecretByID(_ context.Context, id uu if secret.ID == id { q.oauth2ProviderAppSecrets[index] = q.oauth2ProviderAppSecrets[len(q.oauth2ProviderAppSecrets)-1] q.oauth2ProviderAppSecrets = q.oauth2ProviderAppSecrets[:len(q.oauth2ProviderAppSecrets)-1] + + // Cascade delete into tokens. + var keyIDsToDelete []string + var tokens []database.OAuth2ProviderAppToken + for _, token := range q.oauth2ProviderAppTokens { + if token.AppSecretID == id { + keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID) + } else { + tokens = append(tokens, token) + } + } + q.oauth2ProviderAppTokens = tokens + + // Delete from API keys. + var keys []database.APIKey + for _, key := range q.apiKeys { + if !slices.Contains(keyIDsToDelete, key.ID) { + keys = append(keys, key) + } + } + q.apiKeys = keys + return nil } } return sql.ErrNoRows } +func (q *FakeQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error { + err := validateDatabaseType(arg) + if err != nil { + return err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + var keyIDsToDelete []string + var tokens []database.OAuth2ProviderAppToken + for _, token := range q.oauth2ProviderAppTokens { + // Join secrets and keys to see if the token matches. + secretIdx := slices.IndexFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool { + return secret.ID == token.AppSecretID + }) + keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool { + return key.ID == token.APIKeyID + }) + if q.oauth2ProviderAppSecrets[secretIdx].AppID == arg.AppID && q.apiKeys[keyIdx].UserID == arg.UserID { + keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID) + } else { + tokens = append(tokens, token) + } + } + q.oauth2ProviderAppTokens = tokens + + // Cascade delete into API keys. + var keys []database.APIKey + for _, key := range q.apiKeys { + if !slices.Contains(keyIDsToDelete, key.ID) { + keys = append(keys, key) + } + } + q.apiKeys = keys + + return nil +} + func (q *FakeQuerier) DeleteOldProvisionerDaemons(_ context.Context) error { q.mutex.Lock() defer q.mutex.Unlock() @@ -2080,6 +2202,52 @@ func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) return database.OAuth2ProviderApp{}, sql.ErrNoRows } +func (q *FakeQuerier) GetOAuth2ProviderAppCodeByAppIDAndSecret(_ context.Context, arg database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (database.OAuth2ProviderAppCode, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.OAuth2ProviderAppCode{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, code := range q.oauth2ProviderAppCodes { + if bytes.Equal(code.HashedSecret, arg.HashedSecret) && code.AppID == arg.AppID { + return code, nil + } + } + return database.OAuth2ProviderAppCode{}, sql.ErrNoRows +} + +func (q *FakeQuerier) GetOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, code := range q.oauth2ProviderAppCodes { + if code.ID == id { + return code, nil + } + } + return database.OAuth2ProviderAppCode{}, sql.ErrNoRows +} + +func (q *FakeQuerier) GetOAuth2ProviderAppSecretByAppIDAndSecret(_ context.Context, arg database.GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (database.OAuth2ProviderAppSecret, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.OAuth2ProviderAppSecret{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, secret := range q.oauth2ProviderAppSecrets { + if secret.AppID == arg.AppID && bytes.Equal(secret.HashedSecret, arg.HashedSecret) { + return secret, nil + } + } + return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows +} + func (q *FakeQuerier) GetOAuth2ProviderAppSecretByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) { q.mutex.Lock() defer q.mutex.Unlock() @@ -2130,6 +2298,42 @@ func (q *FakeQuerier) GetOAuth2ProviderApps(_ context.Context) ([]database.OAuth return q.oauth2ProviderApps, nil } +func (q *FakeQuerier) GetOAuth2ProviderAppsByUserID(_ context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) { + q.mutex.Lock() + defer q.mutex.Unlock() + + rows := []database.GetOAuth2ProviderAppsByUserIDRow{} + for _, app := range q.oauth2ProviderApps { + tokens := []database.OAuth2ProviderAppToken{} + for _, secret := range q.oauth2ProviderAppSecrets { + if secret.AppID == app.ID { + for _, token := range q.oauth2ProviderAppTokens { + if token.AppSecretID == secret.ID { + keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool { + return key.ID == token.APIKeyID + }) + if q.apiKeys[keyIdx].UserID == userID { + tokens = append(tokens, token) + } + } + } + } + } + if len(tokens) > 0 { + rows = append(rows, database.GetOAuth2ProviderAppsByUserIDRow{ + OAuth2ProviderApp: database.OAuth2ProviderApp{ + CallbackURL: app.CallbackURL, + ID: app.ID, + Icon: app.Icon, + Name: app.Name, + }, + TokenCount: int64(len(tokens)), + }) + } + } + return rows, nil +} + func (q *FakeQuerier) GetOAuthSigningKey(_ context.Context) (string, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -5101,6 +5305,33 @@ func (q *FakeQuerier) InsertOAuth2ProviderApp(_ context.Context, arg database.In return app, nil } +func (q *FakeQuerier) InsertOAuth2ProviderAppCode(_ context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.OAuth2ProviderAppCode{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, app := range q.oauth2ProviderApps { + if app.ID == arg.AppID { + code := database.OAuth2ProviderAppCode{ + ID: arg.ID, + CreatedAt: arg.CreatedAt, + ExpiresAt: arg.ExpiresAt, + HashedSecret: arg.HashedSecret, + UserID: arg.UserID, + AppID: arg.AppID, + } + q.oauth2ProviderAppCodes = append(q.oauth2ProviderAppCodes, code) + return code, nil + } + } + + return database.OAuth2ProviderAppCode{}, sql.ErrNoRows +} + func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) { err := validateDatabaseType(arg) if err != nil { @@ -5127,6 +5358,34 @@ func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg datab return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows } +func (q *FakeQuerier) InsertOAuth2ProviderAppToken(_ context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) { + err := validateDatabaseType(arg) + if err != nil { + return database.OAuth2ProviderAppToken{}, err + } + + q.mutex.Lock() + defer q.mutex.Unlock() + + for _, secret := range q.oauth2ProviderAppSecrets { + if secret.ID == arg.AppSecretID { + //nolint:gosimple // Go wants database.OAuth2ProviderAppToken(arg), but we cannot be sure the structs will remain identical. + token := database.OAuth2ProviderAppToken{ + ID: arg.ID, + CreatedAt: arg.CreatedAt, + ExpiresAt: arg.ExpiresAt, + HashedSecret: arg.HashedSecret, + APIKeyID: arg.APIKeyID, + AppSecretID: arg.AppSecretID, + } + q.oauth2ProviderAppTokens = append(q.oauth2ProviderAppTokens, token) + return token, nil + } + } + + return database.OAuth2ProviderAppToken{}, sql.ErrNoRows +} + func (q *FakeQuerier) InsertOrganization(_ context.Context, arg database.InsertOrganizationParams) (database.Organization, error) { if err := validateDatabaseType(arg); err != nil { return database.Organization{}, err diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 625871500dbeb..84a45f16630e3 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -232,6 +232,20 @@ func (m metricsStore) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.U return r0 } +func (m metricsStore) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error { + start := time.Now() + r0 := m.s.DeleteOAuth2ProviderAppCodeByID(ctx, id) + m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppCodeByID").Observe(time.Since(start).Seconds()) + return r0 +} + +func (m metricsStore) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error { + start := time.Now() + r0 := m.s.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, arg) + m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppCodesByAppAndUserID").Observe(time.Since(start).Seconds()) + return r0 +} + func (m metricsStore) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error { start := time.Now() r0 := m.s.DeleteOAuth2ProviderAppSecretByID(ctx, id) @@ -239,6 +253,13 @@ func (m metricsStore) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id return r0 } +func (m metricsStore) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error { + start := time.Now() + r0 := m.s.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg) + m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppTokensByAppAndUserID").Observe(time.Since(start).Seconds()) + return r0 +} + func (m metricsStore) DeleteOldProvisionerDaemons(ctx context.Context) error { start := time.Now() r0 := m.s.DeleteOldProvisionerDaemons(ctx) @@ -594,6 +615,27 @@ func (m metricsStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID return r0, r1 } +func (m metricsStore) GetOAuth2ProviderAppCodeByAppIDAndSecret(ctx context.Context, arg database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (database.OAuth2ProviderAppCode, error) { + start := time.Now() + r0, r1 := m.s.GetOAuth2ProviderAppCodeByAppIDAndSecret(ctx, arg) + m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppCodeByAppIDAndSecret").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m metricsStore) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) { + start := time.Now() + r0, r1 := m.s.GetOAuth2ProviderAppCodeByID(ctx, id) + m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppCodeByID").Observe(time.Since(start).Seconds()) + return r0, r1 +} + +func (m metricsStore) GetOAuth2ProviderAppSecretByAppIDAndSecret(ctx context.Context, arg database.GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (database.OAuth2ProviderAppSecret, error) { + start := time.Now() + r0, r1 := m.s.GetOAuth2ProviderAppSecretByAppIDAndSecret(ctx, arg) + m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppSecretByAppIDAndSecret").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) { start := time.Now() r0, r1 := m.s.GetOAuth2ProviderAppSecretByID(ctx, id) @@ -615,6 +657,13 @@ func (m metricsStore) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAu return r0, r1 } +func (m metricsStore) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) { + start := time.Now() + r0, r1 := m.s.GetOAuth2ProviderAppsByUserID(ctx, userID) + m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppsByUserID").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) GetOAuthSigningKey(ctx context.Context) (string, error) { start := time.Now() r0, r1 := m.s.GetOAuthSigningKey(ctx) @@ -1390,6 +1439,13 @@ func (m metricsStore) InsertOAuth2ProviderApp(ctx context.Context, arg database. return r0, r1 } +func (m metricsStore) InsertOAuth2ProviderAppCode(ctx context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) { + start := time.Now() + r0, r1 := m.s.InsertOAuth2ProviderAppCode(ctx, arg) + m.queryLatencies.WithLabelValues("InsertOAuth2ProviderAppCode").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) InsertOAuth2ProviderAppSecret(ctx context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) { start := time.Now() r0, r1 := m.s.InsertOAuth2ProviderAppSecret(ctx, arg) @@ -1397,6 +1453,13 @@ func (m metricsStore) InsertOAuth2ProviderAppSecret(ctx context.Context, arg dat return r0, r1 } +func (m metricsStore) InsertOAuth2ProviderAppToken(ctx context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) { + start := time.Now() + r0, r1 := m.s.InsertOAuth2ProviderAppToken(ctx, arg) + m.queryLatencies.WithLabelValues("InsertOAuth2ProviderAppToken").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) InsertOrganization(ctx context.Context, arg database.InsertOrganizationParams) (database.Organization, error) { start := time.Now() organization, err := m.s.InsertOrganization(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index bfb93405f5524..7877c9c93faaf 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -356,6 +356,34 @@ func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppByID(arg0, arg1 any) *go return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppByID), arg0, arg1) } +// DeleteOAuth2ProviderAppCodeByID mocks base method. +func (m *MockStore) DeleteOAuth2ProviderAppCodeByID(arg0 context.Context, arg1 uuid.UUID) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppCodeByID", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteOAuth2ProviderAppCodeByID indicates an expected call of DeleteOAuth2ProviderAppCodeByID. +func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppCodeByID(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppCodeByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppCodeByID), arg0, arg1) +} + +// DeleteOAuth2ProviderAppCodesByAppAndUserID mocks base method. +func (m *MockStore) DeleteOAuth2ProviderAppCodesByAppAndUserID(arg0 context.Context, arg1 database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppCodesByAppAndUserID", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteOAuth2ProviderAppCodesByAppAndUserID indicates an expected call of DeleteOAuth2ProviderAppCodesByAppAndUserID. +func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppCodesByAppAndUserID(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppCodesByAppAndUserID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppCodesByAppAndUserID), arg0, arg1) +} + // DeleteOAuth2ProviderAppSecretByID mocks base method. func (m *MockStore) DeleteOAuth2ProviderAppSecretByID(arg0 context.Context, arg1 uuid.UUID) error { m.ctrl.T.Helper() @@ -370,6 +398,20 @@ func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppSecretByID(arg0, arg1 an return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppSecretByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppSecretByID), arg0, arg1) } +// DeleteOAuth2ProviderAppTokensByAppAndUserID mocks base method. +func (m *MockStore) DeleteOAuth2ProviderAppTokensByAppAndUserID(arg0 context.Context, arg1 database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppTokensByAppAndUserID", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteOAuth2ProviderAppTokensByAppAndUserID indicates an expected call of DeleteOAuth2ProviderAppTokensByAppAndUserID. +func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppTokensByAppAndUserID(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppTokensByAppAndUserID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppTokensByAppAndUserID), arg0, arg1) +} + // DeleteOldProvisionerDaemons mocks base method. func (m *MockStore) DeleteOldProvisionerDaemons(arg0 context.Context) error { m.ctrl.T.Helper() @@ -1175,6 +1217,51 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppByID(arg0, arg1 any) *gomoc return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppByID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppByID), arg0, arg1) } +// GetOAuth2ProviderAppCodeByAppIDAndSecret mocks base method. +func (m *MockStore) GetOAuth2ProviderAppCodeByAppIDAndSecret(arg0 context.Context, arg1 database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (database.OAuth2ProviderAppCode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOAuth2ProviderAppCodeByAppIDAndSecret", arg0, arg1) + ret0, _ := ret[0].(database.OAuth2ProviderAppCode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOAuth2ProviderAppCodeByAppIDAndSecret indicates an expected call of GetOAuth2ProviderAppCodeByAppIDAndSecret. +func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppCodeByAppIDAndSecret(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppCodeByAppIDAndSecret", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppCodeByAppIDAndSecret), arg0, arg1) +} + +// GetOAuth2ProviderAppCodeByID mocks base method. +func (m *MockStore) GetOAuth2ProviderAppCodeByID(arg0 context.Context, arg1 uuid.UUID) (database.OAuth2ProviderAppCode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOAuth2ProviderAppCodeByID", arg0, arg1) + ret0, _ := ret[0].(database.OAuth2ProviderAppCode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOAuth2ProviderAppCodeByID indicates an expected call of GetOAuth2ProviderAppCodeByID. +func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppCodeByID(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppCodeByID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppCodeByID), arg0, arg1) +} + +// GetOAuth2ProviderAppSecretByAppIDAndSecret mocks base method. +func (m *MockStore) GetOAuth2ProviderAppSecretByAppIDAndSecret(arg0 context.Context, arg1 database.GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (database.OAuth2ProviderAppSecret, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOAuth2ProviderAppSecretByAppIDAndSecret", arg0, arg1) + ret0, _ := ret[0].(database.OAuth2ProviderAppSecret) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOAuth2ProviderAppSecretByAppIDAndSecret indicates an expected call of GetOAuth2ProviderAppSecretByAppIDAndSecret. +func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppSecretByAppIDAndSecret(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppSecretByAppIDAndSecret", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppSecretByAppIDAndSecret), arg0, arg1) +} + // GetOAuth2ProviderAppSecretByID mocks base method. func (m *MockStore) GetOAuth2ProviderAppSecretByID(arg0 context.Context, arg1 uuid.UUID) (database.OAuth2ProviderAppSecret, error) { m.ctrl.T.Helper() @@ -1220,6 +1307,21 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderApps(arg0 any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderApps", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderApps), arg0) } +// GetOAuth2ProviderAppsByUserID mocks base method. +func (m *MockStore) GetOAuth2ProviderAppsByUserID(arg0 context.Context, arg1 uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetOAuth2ProviderAppsByUserID", arg0, arg1) + ret0, _ := ret[0].([]database.GetOAuth2ProviderAppsByUserIDRow) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetOAuth2ProviderAppsByUserID indicates an expected call of GetOAuth2ProviderAppsByUserID. +func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppsByUserID(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppsByUserID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppsByUserID), arg0, arg1) +} + // GetOAuthSigningKey mocks base method. func (m *MockStore) GetOAuthSigningKey(arg0 context.Context) (string, error) { m.ctrl.T.Helper() @@ -2925,6 +3027,21 @@ func (mr *MockStoreMockRecorder) InsertOAuth2ProviderApp(arg0, arg1 any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderApp", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderApp), arg0, arg1) } +// InsertOAuth2ProviderAppCode mocks base method. +func (m *MockStore) InsertOAuth2ProviderAppCode(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertOAuth2ProviderAppCode", arg0, arg1) + ret0, _ := ret[0].(database.OAuth2ProviderAppCode) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertOAuth2ProviderAppCode indicates an expected call of InsertOAuth2ProviderAppCode. +func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppCode(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppCode", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppCode), arg0, arg1) +} + // InsertOAuth2ProviderAppSecret mocks base method. func (m *MockStore) InsertOAuth2ProviderAppSecret(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) { m.ctrl.T.Helper() @@ -2940,6 +3057,21 @@ func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppSecret(arg0, arg1 any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppSecret", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppSecret), arg0, arg1) } +// InsertOAuth2ProviderAppToken mocks base method. +func (m *MockStore) InsertOAuth2ProviderAppToken(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertOAuth2ProviderAppToken", arg0, arg1) + ret0, _ := ret[0].(database.OAuth2ProviderAppToken) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertOAuth2ProviderAppToken indicates an expected call of InsertOAuth2ProviderAppToken. +func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppToken(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppToken", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppToken), arg0, arg1) +} + // InsertOrganization mocks base method. func (m *MockStore) InsertOrganization(arg0 context.Context, arg1 database.InsertOrganizationParams) (database.Organization, error) { m.ctrl.T.Helper() diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index f9d1e4311b2b2..f29ae4b47a83f 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -67,7 +67,8 @@ CREATE TYPE login_type AS ENUM ( 'github', 'oidc', 'token', - 'none' + 'none', + 'oauth2_provider_app' ); COMMENT ON TYPE login_type IS 'Specifies the method of authentication. "none" is a special case in which no authentication method is allowed.'; @@ -187,6 +188,17 @@ CREATE TYPE workspace_transition AS ENUM ( 'delete' ); +CREATE FUNCTION delete_deleted_oauth2_provider_app_token_api_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE +BEGIN + DELETE FROM api_keys + WHERE id = OLD.api_key_id; + RETURN OLD; +END; +$$; + CREATE FUNCTION delete_deleted_user_api_keys() RETURNS trigger LANGUAGE plpgsql AS $$ @@ -458,6 +470,17 @@ CREATE SEQUENCE licenses_id_seq ALTER SEQUENCE licenses_id_seq OWNED BY licenses.id; +CREATE TABLE oauth2_provider_app_codes ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + expires_at timestamp with time zone NOT NULL, + hashed_secret bytea NOT NULL, + user_id uuid NOT NULL, + app_id uuid NOT NULL +); + +COMMENT ON TABLE oauth2_provider_app_codes IS 'Codes are meant to be exchanged for access tokens.'; + CREATE TABLE oauth2_provider_app_secrets ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, @@ -469,6 +492,17 @@ CREATE TABLE oauth2_provider_app_secrets ( COMMENT ON COLUMN oauth2_provider_app_secrets.display_secret IS 'The tail end of the original secret so secrets can be differentiated.'; +CREATE TABLE oauth2_provider_app_tokens ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + expires_at timestamp with time zone NOT NULL, + hashed_secret bytea NOT NULL, + app_secret_id uuid NOT NULL, + api_key_id text NOT NULL +); + +COMMENT ON TABLE oauth2_provider_app_tokens IS 'Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them.'; + CREATE TABLE oauth2_provider_apps ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, @@ -1295,12 +1329,24 @@ ALTER TABLE ONLY licenses ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); +ALTER TABLE ONLY oauth2_provider_app_codes + ADD CONSTRAINT oauth2_provider_app_codes_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); + +ALTER TABLE ONLY oauth2_provider_app_codes + ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id); + ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); +ALTER TABLE ONLY oauth2_provider_app_tokens + ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_hashed_secret_key UNIQUE (app_secret_id, hashed_secret); + +ALTER TABLE ONLY oauth2_provider_app_tokens + ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id); + ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name); @@ -1508,6 +1554,8 @@ CREATE TRIGGER tailnet_notify_peer_change AFTER INSERT OR DELETE OR UPDATE ON ta CREATE TRIGGER tailnet_notify_tunnel_change AFTER INSERT OR DELETE OR UPDATE ON tailnet_tunnels FOR EACH ROW EXECUTE FUNCTION tailnet_notify_tunnel_change(); +CREATE TRIGGER trigger_delete_oauth2_provider_app_token AFTER DELETE ON oauth2_provider_app_tokens FOR EACH ROW EXECUTE FUNCTION delete_deleted_oauth2_provider_app_token_api_key(); + CREATE TRIGGER trigger_insert_apikeys BEFORE INSERT ON api_keys FOR EACH ROW EXECUTE FUNCTION insert_apikey_fail_if_user_deleted(); CREATE TRIGGER trigger_update_users AFTER INSERT OR UPDATE ON users FOR EACH ROW WHEN ((new.deleted = true)) EXECUTE FUNCTION delete_deleted_user_api_keys(); @@ -1533,9 +1581,21 @@ ALTER TABLE ONLY group_members ALTER TABLE ONLY groups ADD CONSTRAINT groups_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; +ALTER TABLE ONLY oauth2_provider_app_codes + ADD CONSTRAINT oauth2_provider_app_codes_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE; + +ALTER TABLE ONLY oauth2_provider_app_codes + ADD CONSTRAINT oauth2_provider_app_codes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE; +ALTER TABLE ONLY oauth2_provider_app_tokens + ADD CONSTRAINT oauth2_provider_app_tokens_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE; + +ALTER TABLE ONLY oauth2_provider_app_tokens + ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_fkey FOREIGN KEY (app_secret_id) REFERENCES oauth2_provider_app_secrets(id) ON DELETE CASCADE; + 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/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go index 5dc75af93f1be..1fecc5b82c491 100644 --- a/coderd/database/foreign_key_constraint.go +++ b/coderd/database/foreign_key_constraint.go @@ -13,7 +13,11 @@ const ( ForeignKeyGroupMembersGroupID ForeignKeyConstraint = "group_members_group_id_fkey" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_group_id_fkey FOREIGN KEY (group_id) REFERENCES groups(id) ON DELETE CASCADE; ForeignKeyGroupMembersUserID ForeignKeyConstraint = "group_members_user_id_fkey" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; ForeignKeyGroupsOrganizationID ForeignKeyConstraint = "groups_organization_id_fkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; + ForeignKeyOauth2ProviderAppCodesAppID ForeignKeyConstraint = "oauth2_provider_app_codes_app_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE; + ForeignKeyOauth2ProviderAppCodesUserID ForeignKeyConstraint = "oauth2_provider_app_codes_user_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; ForeignKeyOauth2ProviderAppSecretsAppID ForeignKeyConstraint = "oauth2_provider_app_secrets_app_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE; + ForeignKeyOauth2ProviderAppTokensAPIKeyID ForeignKeyConstraint = "oauth2_provider_app_tokens_api_key_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE; + ForeignKeyOauth2ProviderAppTokensAppSecretID ForeignKeyConstraint = "oauth2_provider_app_tokens_app_secret_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_fkey FOREIGN KEY (app_secret_id) REFERENCES oauth2_provider_app_secrets(id) ON DELETE CASCADE; ForeignKeyOrganizationMembersOrganizationIDUUID ForeignKeyConstraint = "organization_members_organization_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE; ForeignKeyOrganizationMembersUserIDUUID ForeignKeyConstraint = "organization_members_user_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; ForeignKeyParameterSchemasJobID ForeignKeyConstraint = "parameter_schemas_job_id_fkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE; diff --git a/coderd/database/migrations/000186_oauth2_provider_codes.down.sql b/coderd/database/migrations/000186_oauth2_provider_codes.down.sql new file mode 100644 index 0000000000000..09b1ee274a341 --- /dev/null +++ b/coderd/database/migrations/000186_oauth2_provider_codes.down.sql @@ -0,0 +1,8 @@ +DROP TRIGGER IF EXISTS trigger_delete_oauth2_provider_app_token ON oauth2_provider_app_tokens; +DROP FUNCTION IF EXISTS delete_deleted_oauth2_provider_app_token_api_key; + +DROP TABLE oauth2_provider_app_tokens; +DROP TABLE oauth2_provider_app_codes; + +-- It is not possible to drop enum values from enum types, so the UP on +-- login_type has "IF NOT EXISTS". diff --git a/coderd/database/migrations/000186_oauth2_provider_codes.up.sql b/coderd/database/migrations/000186_oauth2_provider_codes.up.sql new file mode 100644 index 0000000000000..48295361a0238 --- /dev/null +++ b/coderd/database/migrations/000186_oauth2_provider_codes.up.sql @@ -0,0 +1,44 @@ +CREATE TABLE oauth2_provider_app_codes ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + expires_at timestamp with time zone NOT NULL, + hashed_secret bytea NOT NULL, + user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE, + app_id uuid NOT NULL REFERENCES oauth2_provider_apps (id) ON DELETE CASCADE, + PRIMARY KEY (id), + UNIQUE(app_id, hashed_secret) +); + +COMMENT ON TABLE oauth2_provider_app_codes IS 'Codes are meant to be exchanged for access tokens.'; + +CREATE TABLE oauth2_provider_app_tokens ( + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + expires_at timestamp with time zone NOT NULL, + hashed_secret bytea NOT NULL, + app_secret_id uuid NOT NULL REFERENCES oauth2_provider_app_secrets (id) ON DELETE CASCADE, + api_key_id text NOT NULL REFERENCES api_keys (id) ON DELETE CASCADE, + PRIMARY KEY (id), + UNIQUE(app_secret_id, hashed_secret) +); + +COMMENT ON TABLE oauth2_provider_app_tokens IS 'Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them.'; + +-- When we delete a token, delete the API key associated with it. +CREATE FUNCTION delete_deleted_oauth2_provider_app_token_api_key() RETURNS trigger + LANGUAGE plpgsql + AS $$ +DECLARE +BEGIN + DELETE FROM api_keys + WHERE id = OLD.api_key_id; + RETURN OLD; +END; +$$; + +CREATE TRIGGER trigger_delete_oauth2_provider_app_token +AFTER DELETE ON oauth2_provider_app_tokens +FOR EACH ROW +EXECUTE PROCEDURE delete_deleted_oauth2_provider_app_token_api_key(); + +ALTER TYPE login_type ADD VALUE IF NOT EXISTS 'oauth2_provider_app'; diff --git a/coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql b/coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql new file mode 100644 index 0000000000000..f4532a4523c6d --- /dev/null +++ b/coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql @@ -0,0 +1,21 @@ +INSERT INTO oauth2_provider_app_codes + (id, created_at, expires_at, hashed_secret, user_id, app_id) +VALUES ( + 'c0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', + '2023-06-15 10:23:54+00', + '2023-06-15 10:23:54+00', + CAST('abcdefg' AS bytea), + '0ed9befc-4911-4ccf-a8e2-559bf72daa94', + 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11' +); + +INSERT INTO oauth2_provider_app_tokens + (id, created_at, expires_at, hashed_secret, app_secret_id, api_key_id) +VALUES ( + 'd0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', + '2023-06-15 10:25:33+00', + '2023-12-15 11:40:20+00', + CAST('abcdefg' AS bytea), + 'b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', + 'peuLZhMXt4' +); diff --git a/coderd/database/modelmethods.go b/coderd/database/modelmethods.go index 685c138c95288..3721eac83dd75 100644 --- a/coderd/database/modelmethods.go +++ b/coderd/database/modelmethods.go @@ -277,6 +277,22 @@ func (l License) RBACObject() rbac.Object { return rbac.ResourceLicense.WithIDString(strconv.FormatInt(int64(l.ID), 10)) } +func (c OAuth2ProviderAppCode) RBACObject() rbac.Object { + return rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(c.UserID.String()) +} + +func (OAuth2ProviderAppSecret) RBACObject() rbac.Object { + return rbac.ResourceOAuth2ProviderAppSecret +} + +func (OAuth2ProviderApp) RBACObject() rbac.Object { + return rbac.ResourceOAuth2ProviderApp +} + +func (a GetOAuth2ProviderAppsByUserIDRow) RBACObject() rbac.Object { + return a.OAuth2ProviderApp.RBACObject() +} + type WorkspaceAgentConnectionStatus struct { Status WorkspaceAgentStatus `json:"status"` FirstConnectedAt *time.Time `json:"first_connected_at"` diff --git a/coderd/database/models.go b/coderd/database/models.go index 5308f88b35a79..29ec66275e875 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -593,11 +593,12 @@ func AllLogSourceValues() []LogSource { type LoginType string const ( - LoginTypePassword LoginType = "password" - LoginTypeGithub LoginType = "github" - LoginTypeOIDC LoginType = "oidc" - LoginTypeToken LoginType = "token" - LoginTypeNone LoginType = "none" + LoginTypePassword LoginType = "password" + LoginTypeGithub LoginType = "github" + LoginTypeOIDC LoginType = "oidc" + LoginTypeToken LoginType = "token" + LoginTypeNone LoginType = "none" + LoginTypeOAuth2ProviderApp LoginType = "oauth2_provider_app" ) func (e *LoginType) Scan(src interface{}) error { @@ -641,7 +642,8 @@ func (e LoginType) Valid() bool { LoginTypeGithub, LoginTypeOIDC, LoginTypeToken, - LoginTypeNone: + LoginTypeNone, + LoginTypeOAuth2ProviderApp: return true } return false @@ -654,6 +656,7 @@ func AllLoginTypeValues() []LoginType { LoginTypeOIDC, LoginTypeToken, LoginTypeNone, + LoginTypeOAuth2ProviderApp, } } @@ -1798,6 +1801,16 @@ type OAuth2ProviderApp struct { CallbackURL string `db:"callback_url" json:"callback_url"` } +// Codes are meant to be exchanged for access tokens. +type OAuth2ProviderAppCode struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` + UserID uuid.UUID `db:"user_id" json:"user_id"` + AppID uuid.UUID `db:"app_id" json:"app_id"` +} + type OAuth2ProviderAppSecret struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` @@ -1808,6 +1821,16 @@ type OAuth2ProviderAppSecret struct { AppID uuid.UUID `db:"app_id" json:"app_id"` } +// Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them. +type OAuth2ProviderAppToken struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` + AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` + APIKeyID string `db:"api_key_id" json:"api_key_id"` +} + type Organization struct { ID uuid.UUID `db:"id" json:"id"` Name string `db:"name" json:"name"` diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 8947ba185d14d..7f839a1e61074 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -59,7 +59,10 @@ type sqlcQuerier interface { DeleteGroupMembersByOrgAndUser(ctx context.Context, arg DeleteGroupMembersByOrgAndUserParams) error DeleteLicense(ctx context.Context, id int32) (int32, error) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) error + DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error + DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error + DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error // Delete provisioner daemons that have been created at least a week ago // and have not connected to coderd since a week. // A provisioner daemon with "zeroed" last_seen_at column indicates possible @@ -126,9 +129,13 @@ type sqlcQuerier interface { GetLicenses(ctx context.Context) ([]License, error) GetLogoURL(ctx context.Context) (string, error) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error) + GetOAuth2ProviderAppCodeByAppIDAndSecret(ctx context.Context, arg GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (OAuth2ProviderAppCode, error) + GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error) + GetOAuth2ProviderAppSecretByAppIDAndSecret(ctx context.Context, arg GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (OAuth2ProviderAppSecret, error) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppSecret, error) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]OAuth2ProviderAppSecret, error) GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2ProviderApp, error) + GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]GetOAuth2ProviderAppsByUserIDRow, error) GetOAuthSigningKey(ctx context.Context) (string, error) GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error) GetOrganizationByName(ctx context.Context, name string) (Organization, error) @@ -283,7 +290,9 @@ type sqlcQuerier interface { // If the name conflicts, do nothing. InsertMissingGroups(ctx context.Context, arg InsertMissingGroupsParams) ([]Group, error) InsertOAuth2ProviderApp(ctx context.Context, arg InsertOAuth2ProviderAppParams) (OAuth2ProviderApp, error) + InsertOAuth2ProviderAppCode(ctx context.Context, arg InsertOAuth2ProviderAppCodeParams) (OAuth2ProviderAppCode, error) InsertOAuth2ProviderAppSecret(ctx context.Context, arg InsertOAuth2ProviderAppSecretParams) (OAuth2ProviderAppSecret, error) + InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error) InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error) InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error) InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index b18662df3537e..4741c3a1c7803 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2620,6 +2620,29 @@ func (q *sqlQuerier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UU return err } +const deleteOAuth2ProviderAppCodeByID = `-- name: DeleteOAuth2ProviderAppCodeByID :exec +DELETE FROM oauth2_provider_app_codes WHERE id = $1 +` + +func (q *sqlQuerier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error { + _, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodeByID, id) + return err +} + +const deleteOAuth2ProviderAppCodesByAppAndUserID = `-- name: DeleteOAuth2ProviderAppCodesByAppAndUserID :exec +DELETE FROM oauth2_provider_app_codes WHERE app_id = $1 AND user_id = $2 +` + +type DeleteOAuth2ProviderAppCodesByAppAndUserIDParams struct { + AppID uuid.UUID `db:"app_id" json:"app_id"` + UserID uuid.UUID `db:"user_id" json:"user_id"` +} + +func (q *sqlQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error { + _, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodesByAppAndUserID, arg.AppID, arg.UserID) + return err +} + const deleteOAuth2ProviderAppSecretByID = `-- name: DeleteOAuth2ProviderAppSecretByID :exec DELETE FROM oauth2_provider_app_secrets WHERE id = $1 ` @@ -2629,6 +2652,26 @@ func (q *sqlQuerier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id u return err } +const deleteOAuth2ProviderAppTokensByAppAndUserID = `-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec +DELETE FROM oauth2_provider_app_tokens +USING oauth2_provider_app_secrets, api_keys +WHERE + oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id + AND api_keys.id = oauth2_provider_app_tokens.api_key_id + AND oauth2_provider_app_secrets.app_id = $1 + AND api_keys.user_id = $2 +` + +type DeleteOAuth2ProviderAppTokensByAppAndUserIDParams struct { + AppID uuid.UUID `db:"app_id" json:"app_id"` + UserID uuid.UUID `db:"user_id" json:"user_id"` +} + +func (q *sqlQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error { + _, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppTokensByAppAndUserID, arg.AppID, arg.UserID) + return err +} + const getOAuth2ProviderAppByID = `-- name: GetOAuth2ProviderAppByID :one SELECT id, created_at, updated_at, name, icon, callback_url FROM oauth2_provider_apps WHERE id = $1 ` @@ -2647,6 +2690,70 @@ func (q *sqlQuerier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) return i, err } +const getOAuth2ProviderAppCodeByAppIDAndSecret = `-- name: GetOAuth2ProviderAppCodeByAppIDAndSecret :one +SELECT id, created_at, expires_at, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE app_id = $1 AND hashed_secret = $2 +` + +type GetOAuth2ProviderAppCodeByAppIDAndSecretParams struct { + AppID uuid.UUID `db:"app_id" json:"app_id"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` +} + +func (q *sqlQuerier) GetOAuth2ProviderAppCodeByAppIDAndSecret(ctx context.Context, arg GetOAuth2ProviderAppCodeByAppIDAndSecretParams) (OAuth2ProviderAppCode, error) { + row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByAppIDAndSecret, arg.AppID, arg.HashedSecret) + var i OAuth2ProviderAppCode + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.ExpiresAt, + &i.HashedSecret, + &i.UserID, + &i.AppID, + ) + return i, err +} + +const getOAuth2ProviderAppCodeByID = `-- name: GetOAuth2ProviderAppCodeByID :one +SELECT id, created_at, expires_at, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE id = $1 +` + +func (q *sqlQuerier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error) { + row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByID, id) + var i OAuth2ProviderAppCode + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.ExpiresAt, + &i.HashedSecret, + &i.UserID, + &i.AppID, + ) + return i, err +} + +const getOAuth2ProviderAppSecretByAppIDAndSecret = `-- name: GetOAuth2ProviderAppSecretByAppIDAndSecret :one +SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id FROM oauth2_provider_app_secrets WHERE app_id = $1 AND hashed_secret = $2 +` + +type GetOAuth2ProviderAppSecretByAppIDAndSecretParams struct { + AppID uuid.UUID `db:"app_id" json:"app_id"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` +} + +func (q *sqlQuerier) GetOAuth2ProviderAppSecretByAppIDAndSecret(ctx context.Context, arg GetOAuth2ProviderAppSecretByAppIDAndSecretParams) (OAuth2ProviderAppSecret, error) { + row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppSecretByAppIDAndSecret, arg.AppID, arg.HashedSecret) + var i OAuth2ProviderAppSecret + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.LastUsedAt, + &i.HashedSecret, + &i.DisplaySecret, + &i.AppID, + ) + return i, err +} + const getOAuth2ProviderAppSecretByID = `-- name: GetOAuth2ProviderAppSecretByID :one SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id FROM oauth2_provider_app_secrets WHERE id = $1 ` @@ -2733,6 +2840,56 @@ func (q *sqlQuerier) GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2Provide return items, nil } +const getOAuth2ProviderAppsByUserID = `-- name: GetOAuth2ProviderAppsByUserID :many +SELECT COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, + oauth2_provider_apps.id, oauth2_provider_apps.created_at, oauth2_provider_apps.updated_at, oauth2_provider_apps.name, oauth2_provider_apps.icon, oauth2_provider_apps.callback_url +FROM oauth2_provider_app_tokens + INNER JOIN oauth2_provider_app_secrets + ON oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id + INNER JOIN oauth2_provider_apps + ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id + INNER JOIN api_keys + ON api_keys.id = oauth2_provider_app_tokens.api_key_id +WHERE api_keys.user_id = $1 +GROUP BY oauth2_provider_apps.id +` + +type GetOAuth2ProviderAppsByUserIDRow struct { + TokenCount int64 `db:"token_count" json:"token_count"` + OAuth2ProviderApp OAuth2ProviderApp `db:"oauth2_provider_app" json:"oauth2_provider_app"` +} + +func (q *sqlQuerier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]GetOAuth2ProviderAppsByUserIDRow, error) { + rows, err := q.db.QueryContext(ctx, getOAuth2ProviderAppsByUserID, userID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []GetOAuth2ProviderAppsByUserIDRow + for rows.Next() { + var i GetOAuth2ProviderAppsByUserIDRow + if err := rows.Scan( + &i.TokenCount, + &i.OAuth2ProviderApp.ID, + &i.OAuth2ProviderApp.CreatedAt, + &i.OAuth2ProviderApp.UpdatedAt, + &i.OAuth2ProviderApp.Name, + &i.OAuth2ProviderApp.Icon, + &i.OAuth2ProviderApp.CallbackURL, + ); 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 insertOAuth2ProviderApp = `-- name: InsertOAuth2ProviderApp :one INSERT INTO oauth2_provider_apps ( id, @@ -2781,6 +2938,54 @@ func (q *sqlQuerier) InsertOAuth2ProviderApp(ctx context.Context, arg InsertOAut return i, err } +const insertOAuth2ProviderAppCode = `-- name: InsertOAuth2ProviderAppCode :one +INSERT INTO oauth2_provider_app_codes ( + id, + created_at, + expires_at, + hashed_secret, + app_id, + user_id +) VALUES( + $1, + $2, + $3, + $4, + $5, + $6 +) RETURNING id, created_at, expires_at, hashed_secret, user_id, app_id +` + +type InsertOAuth2ProviderAppCodeParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` + AppID uuid.UUID `db:"app_id" json:"app_id"` + UserID uuid.UUID `db:"user_id" json:"user_id"` +} + +func (q *sqlQuerier) InsertOAuth2ProviderAppCode(ctx context.Context, arg InsertOAuth2ProviderAppCodeParams) (OAuth2ProviderAppCode, error) { + row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppCode, + arg.ID, + arg.CreatedAt, + arg.ExpiresAt, + arg.HashedSecret, + arg.AppID, + arg.UserID, + ) + var i OAuth2ProviderAppCode + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.ExpiresAt, + &i.HashedSecret, + &i.UserID, + &i.AppID, + ) + return i, err +} + const insertOAuth2ProviderAppSecret = `-- name: InsertOAuth2ProviderAppSecret :one INSERT INTO oauth2_provider_app_secrets ( id, @@ -2825,6 +3030,54 @@ func (q *sqlQuerier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg Inse return i, err } +const insertOAuth2ProviderAppToken = `-- name: InsertOAuth2ProviderAppToken :one +INSERT INTO oauth2_provider_app_tokens ( + id, + created_at, + expires_at, + hashed_secret, + app_secret_id, + api_key_id +) VALUES( + $1, + $2, + $3, + $4, + $5, + $6 +) RETURNING id, created_at, expires_at, hashed_secret, app_secret_id, api_key_id +` + +type InsertOAuth2ProviderAppTokenParams struct { + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` + AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` + APIKeyID string `db:"api_key_id" json:"api_key_id"` +} + +func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error) { + row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppToken, + arg.ID, + arg.CreatedAt, + arg.ExpiresAt, + arg.HashedSecret, + arg.AppSecretID, + arg.APIKeyID, + ) + var i OAuth2ProviderAppToken + err := row.Scan( + &i.ID, + &i.CreatedAt, + &i.ExpiresAt, + &i.HashedSecret, + &i.AppSecretID, + &i.APIKeyID, + ) + return i, err +} + const updateOAuth2ProviderAppByID = `-- name: UpdateOAuth2ProviderAppByID :one UPDATE oauth2_provider_apps SET updated_at = $2, diff --git a/coderd/database/queries/oauth2.sql b/coderd/database/queries/oauth2.sql index cd9a150d0bf38..c0c0c7dab6928 100644 --- a/coderd/database/queries/oauth2.sql +++ b/coderd/database/queries/oauth2.sql @@ -38,6 +38,9 @@ SELECT * FROM oauth2_provider_app_secrets WHERE id = $1; -- name: GetOAuth2ProviderAppSecretsByAppID :many SELECT * FROM oauth2_provider_app_secrets WHERE app_id = $1 ORDER BY (created_at, id) ASC; +-- name: GetOAuth2ProviderAppSecretByAppIDAndSecret :one +SELECT * FROM oauth2_provider_app_secrets WHERE app_id = $1 AND hashed_secret = $2; + -- name: InsertOAuth2ProviderAppSecret :one INSERT INTO oauth2_provider_app_secrets ( id, @@ -60,3 +63,71 @@ WHERE id = $1 RETURNING *; -- name: DeleteOAuth2ProviderAppSecretByID :exec DELETE FROM oauth2_provider_app_secrets WHERE id = $1; + +-- name: GetOAuth2ProviderAppCodeByID :one +SELECT * FROM oauth2_provider_app_codes WHERE id = $1; + +-- name: GetOAuth2ProviderAppCodeByAppIDAndSecret :one +SELECT * FROM oauth2_provider_app_codes WHERE app_id = $1 AND hashed_secret = $2; + +-- name: InsertOAuth2ProviderAppCode :one +INSERT INTO oauth2_provider_app_codes ( + id, + created_at, + expires_at, + hashed_secret, + app_id, + user_id +) VALUES( + $1, + $2, + $3, + $4, + $5, + $6 +) RETURNING *; + +-- name: DeleteOAuth2ProviderAppCodeByID :exec +DELETE FROM oauth2_provider_app_codes WHERE id = $1; + +-- name: DeleteOAuth2ProviderAppCodesByAppAndUserID :exec +DELETE FROM oauth2_provider_app_codes WHERE app_id = $1 AND user_id = $2; + +-- name: InsertOAuth2ProviderAppToken :one +INSERT INTO oauth2_provider_app_tokens ( + id, + created_at, + expires_at, + hashed_secret, + app_secret_id, + api_key_id +) VALUES( + $1, + $2, + $3, + $4, + $5, + $6 +) RETURNING *; + +-- name: GetOAuth2ProviderAppsByUserID :many +SELECT COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, + sqlc.embed(oauth2_provider_apps) +FROM oauth2_provider_app_tokens + INNER JOIN oauth2_provider_app_secrets + ON oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id + INNER JOIN oauth2_provider_apps + ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id + INNER JOIN api_keys + ON api_keys.id = oauth2_provider_app_tokens.api_key_id +WHERE api_keys.user_id = $1 +GROUP BY oauth2_provider_apps.id; + +-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec +DELETE FROM oauth2_provider_app_tokens +USING oauth2_provider_app_secrets, api_keys +WHERE + oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id + AND api_keys.id = oauth2_provider_app_tokens.api_key_id + AND oauth2_provider_app_secrets.app_id = $1 + AND api_keys.user_id = $2; diff --git a/coderd/database/sqlc.yaml b/coderd/database/sqlc.yaml index 49140d597ae9e..621946e7b4913 100644 --- a/coderd/database/sqlc.yaml +++ b/coderd/database/sqlc.yaml @@ -99,4 +99,8 @@ sql: display_app_ssh_helper: DisplayAppSSHHelper oauth2_provider_app: OAuth2ProviderApp oauth2_provider_app_secret: OAuth2ProviderAppSecret + oauth2_provider_app_code: OAuth2ProviderAppCode + oauth2_provider_app_token: OAuth2ProviderAppToken + api_key_id: APIKeyID callback_url: CallbackURL + login_type_oauth2_provider_app: LoginTypeOAuth2ProviderApp diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index f397692f1d6d1..039172c012a35 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -6,75 +6,79 @@ type UniqueConstraint string // UniqueConstraint enums. 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); - 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); - UniqueDbcryptKeysRevokedKeyDigestKey UniqueConstraint = "dbcrypt_keys_revoked_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_revoked_key_digest_key UNIQUE (revoked_key_digest); - UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); - UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); - UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); - UniqueGitSSHKeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); - UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); - UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); - UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); - UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt); - UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppSecretsAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_secrets_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); - UniqueOauth2ProviderAppSecretsPkey UniqueConstraint = "oauth2_provider_app_secrets_pkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppsNameKey UniqueConstraint = "oauth2_provider_apps_name_key" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name); - UniqueOauth2ProviderAppsPkey UniqueConstraint = "oauth2_provider_apps_pkey" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_pkey PRIMARY KEY (id); - UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id); - UniqueOrganizationsPkey UniqueConstraint = "organizations_pkey" // ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); - UniqueParameterSchemasJobIDNameKey UniqueConstraint = "parameter_schemas_job_id_name_key" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); - UniqueParameterSchemasPkey UniqueConstraint = "parameter_schemas_pkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id); - UniqueParameterValuesPkey UniqueConstraint = "parameter_values_pkey" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id); - UniqueParameterValuesScopeIDNameKey UniqueConstraint = "parameter_values_scope_id_name_key" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name); - UniqueProvisionerDaemonsPkey UniqueConstraint = "provisioner_daemons_pkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id); - UniqueProvisionerJobLogsPkey UniqueConstraint = "provisioner_job_logs_pkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id); - UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); - UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); - UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetClientSubscriptionsPkey UniqueConstraint = "tailnet_client_subscriptions_pkey" // ALTER TABLE ONLY tailnet_client_subscriptions ADD CONSTRAINT tailnet_client_subscriptions_pkey PRIMARY KEY (client_id, coordinator_id, agent_id); - UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); - UniqueTailnetPeersPkey UniqueConstraint = "tailnet_peers_pkey" // ALTER TABLE ONLY tailnet_peers ADD CONSTRAINT tailnet_peers_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetTunnelsPkey UniqueConstraint = "tailnet_tunnels_pkey" // ALTER TABLE ONLY tailnet_tunnels ADD CONSTRAINT tailnet_tunnels_pkey PRIMARY KEY (coordinator_id, src_id, dst_id); - UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); - UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); - UniqueTemplateVersionsPkey UniqueConstraint = "template_versions_pkey" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_pkey PRIMARY KEY (id); - UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); - UniqueTemplatesPkey UniqueConstraint = "templates_pkey" // ALTER TABLE ONLY templates ADD CONSTRAINT templates_pkey PRIMARY KEY (id); - UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type); - UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); - UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); - UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); - UniqueWorkspaceAgentStartupLogsPkey UniqueConstraint = "workspace_agent_startup_logs_pkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id); - UniqueWorkspaceAgentsPkey UniqueConstraint = "workspace_agents_pkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id); - UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id); - UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); - UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); - UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id); - UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); - UniqueWorkspaceBuildsJobIDKey UniqueConstraint = "workspace_builds_job_id_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id); - UniqueWorkspaceBuildsPkey UniqueConstraint = "workspace_builds_pkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id); - UniqueWorkspaceBuildsWorkspaceIDBuildNumberKey UniqueConstraint = "workspace_builds_workspace_id_build_number_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_build_number_key UNIQUE (workspace_id, build_number); - UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); - UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); - UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); - UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); - UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); - UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); - UniqueIndexAPIKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); - UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); - UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); - UniqueIndexProvisionerDaemonsNameOwnerKey UniqueConstraint = "idx_provisioner_daemons_name_owner_key" // CREATE UNIQUE INDEX idx_provisioner_daemons_name_owner_key ON provisioner_daemons USING btree (name, lower(COALESCE((tags ->> 'owner'::text), ''::text))); - UniqueIndexUsersEmail UniqueConstraint = "idx_users_email" // CREATE UNIQUE INDEX idx_users_email ON users USING btree (email) WHERE (deleted = false); - UniqueIndexUsersUsername UniqueConstraint = "idx_users_username" // CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (deleted = false); - UniqueTemplatesOrganizationIDNameIndex UniqueConstraint = "templates_organization_id_name_idx" // CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree (organization_id, lower((name)::text)) WHERE (deleted = false); - UniqueUsersEmailLowerIndex UniqueConstraint = "users_email_lower_idx" // CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false); - UniqueUsersUsernameLowerIndex UniqueConstraint = "users_username_lower_idx" // CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false); - UniqueWorkspaceProxiesLowerNameIndex UniqueConstraint = "workspace_proxies_lower_name_idx" // CREATE UNIQUE INDEX workspace_proxies_lower_name_idx ON workspace_proxies USING btree (lower(name)) WHERE (deleted = false); - UniqueWorkspacesOwnerIDLowerIndex UniqueConstraint = "workspaces_owner_id_lower_idx" // CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false); + 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); + 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); + UniqueDbcryptKeysRevokedKeyDigestKey UniqueConstraint = "dbcrypt_keys_revoked_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_revoked_key_digest_key UNIQUE (revoked_key_digest); + UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); + UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); + UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); + UniqueGitSSHKeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); + UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); + UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); + UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); + UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt); + UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppCodesAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_codes_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); + UniqueOauth2ProviderAppCodesPkey UniqueConstraint = "oauth2_provider_app_codes_pkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppSecretsAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_secrets_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); + UniqueOauth2ProviderAppSecretsPkey UniqueConstraint = "oauth2_provider_app_secrets_pkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppTokensAppSecretIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_tokens_app_secret_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_hashed_secret_key UNIQUE (app_secret_id, hashed_secret); + UniqueOauth2ProviderAppTokensPkey UniqueConstraint = "oauth2_provider_app_tokens_pkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppsNameKey UniqueConstraint = "oauth2_provider_apps_name_key" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name); + UniqueOauth2ProviderAppsPkey UniqueConstraint = "oauth2_provider_apps_pkey" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_pkey PRIMARY KEY (id); + UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id); + UniqueOrganizationsPkey UniqueConstraint = "organizations_pkey" // ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); + UniqueParameterSchemasJobIDNameKey UniqueConstraint = "parameter_schemas_job_id_name_key" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); + UniqueParameterSchemasPkey UniqueConstraint = "parameter_schemas_pkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id); + UniqueParameterValuesPkey UniqueConstraint = "parameter_values_pkey" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id); + UniqueParameterValuesScopeIDNameKey UniqueConstraint = "parameter_values_scope_id_name_key" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name); + UniqueProvisionerDaemonsPkey UniqueConstraint = "provisioner_daemons_pkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id); + UniqueProvisionerJobLogsPkey UniqueConstraint = "provisioner_job_logs_pkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id); + UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); + UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); + UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetClientSubscriptionsPkey UniqueConstraint = "tailnet_client_subscriptions_pkey" // ALTER TABLE ONLY tailnet_client_subscriptions ADD CONSTRAINT tailnet_client_subscriptions_pkey PRIMARY KEY (client_id, coordinator_id, agent_id); + UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); + UniqueTailnetPeersPkey UniqueConstraint = "tailnet_peers_pkey" // ALTER TABLE ONLY tailnet_peers ADD CONSTRAINT tailnet_peers_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetTunnelsPkey UniqueConstraint = "tailnet_tunnels_pkey" // ALTER TABLE ONLY tailnet_tunnels ADD CONSTRAINT tailnet_tunnels_pkey PRIMARY KEY (coordinator_id, src_id, dst_id); + UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); + UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); + UniqueTemplateVersionsPkey UniqueConstraint = "template_versions_pkey" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_pkey PRIMARY KEY (id); + UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); + UniqueTemplatesPkey UniqueConstraint = "templates_pkey" // ALTER TABLE ONLY templates ADD CONSTRAINT templates_pkey PRIMARY KEY (id); + UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type); + UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); + UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); + UniqueWorkspaceAgentStartupLogsPkey UniqueConstraint = "workspace_agent_startup_logs_pkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentsPkey UniqueConstraint = "workspace_agents_pkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id); + UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id); + UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); + UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); + UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id); + UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); + UniqueWorkspaceBuildsJobIDKey UniqueConstraint = "workspace_builds_job_id_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id); + UniqueWorkspaceBuildsPkey UniqueConstraint = "workspace_builds_pkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id); + UniqueWorkspaceBuildsWorkspaceIDBuildNumberKey UniqueConstraint = "workspace_builds_workspace_id_build_number_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_build_number_key UNIQUE (workspace_id, build_number); + UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); + UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); + UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); + UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); + UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); + UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); + UniqueIndexAPIKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); + UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); + UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); + UniqueIndexProvisionerDaemonsNameOwnerKey UniqueConstraint = "idx_provisioner_daemons_name_owner_key" // CREATE UNIQUE INDEX idx_provisioner_daemons_name_owner_key ON provisioner_daemons USING btree (name, lower(COALESCE((tags ->> 'owner'::text), ''::text))); + UniqueIndexUsersEmail UniqueConstraint = "idx_users_email" // CREATE UNIQUE INDEX idx_users_email ON users USING btree (email) WHERE (deleted = false); + UniqueIndexUsersUsername UniqueConstraint = "idx_users_username" // CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (deleted = false); + UniqueTemplatesOrganizationIDNameIndex UniqueConstraint = "templates_organization_id_name_idx" // CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree (organization_id, lower((name)::text)) WHERE (deleted = false); + UniqueUsersEmailLowerIndex UniqueConstraint = "users_email_lower_idx" // CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false); + UniqueUsersUsernameLowerIndex UniqueConstraint = "users_username_lower_idx" // CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false); + UniqueWorkspaceProxiesLowerNameIndex UniqueConstraint = "workspace_proxies_lower_name_idx" // CREATE UNIQUE INDEX workspace_proxies_lower_name_idx ON workspace_proxies USING btree (lower(name)) WHERE (deleted = false); + UniqueWorkspacesOwnerIDLowerIndex UniqueConstraint = "workspaces_owner_id_lower_idx" // CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false); ) diff --git a/coderd/rbac/object.go b/coderd/rbac/object.go index 373c5b6b2df17..f130142f48f65 100644 --- a/coderd/rbac/object.go +++ b/coderd/rbac/object.go @@ -207,12 +207,20 @@ var ( Type: "oauth2_app", } - // ResourceOAuth2ProviderAppSecrets CRUD. + // ResourceOAuth2ProviderAppSecret CRUD. // create/delete = Make or delete an OAuth2 app secret. // update = Update last used date. // read = Read OAuth2 app hashed or truncated secret. ResourceOAuth2ProviderAppSecret = Object{ - Type: "oauth2_app_secrets", + Type: "oauth2_app_secret", + } + + // ResourceOAuth2ProviderAppCodeToken CRUD. + // create/delete = Make or delete an OAuth2 app code or token. + // update = None + // read = Check if OAuth2 app code or token exists. + ResourceOAuth2ProviderAppCodeToken = Object{ + Type: "oauth2_app_code_token", } ) diff --git a/coderd/rbac/object_gen.go b/coderd/rbac/object_gen.go index aadf3fa1edcb7..4833a14b30239 100644 --- a/coderd/rbac/object_gen.go +++ b/coderd/rbac/object_gen.go @@ -12,6 +12,7 @@ func AllResources() []Object { ResourceGroup, ResourceLicense, ResourceOAuth2ProviderApp, + ResourceOAuth2ProviderAppCodeToken, ResourceOAuth2ProviderAppSecret, ResourceOrgRoleAssignment, ResourceOrganization, diff --git a/coderd/rbac/roles.go b/coderd/rbac/roles.go index 7f8e0b2759547..5c8af956945b7 100644 --- a/coderd/rbac/roles.go +++ b/coderd/rbac/roles.go @@ -148,6 +148,8 @@ func ReloadBuiltinRoles(opts *RoleOptions) { ResourceRoleAssignment.Type: {ActionRead}, // All users can see the provisioner daemons. ResourceProvisionerDaemon.Type: {ActionRead}, + // All users can see OAuth2 provider applications. + ResourceOAuth2ProviderApp.Type: {ActionRead}, }), Org: map[string][]Permission{}, User: append(allPermsExcept(ResourceWorkspaceDormant, ResourceUser, ResourceOrganizationMember), From 7b118ac8d3ebb7294d10ae15efaf5b1c7104396d Mon Sep 17 00:00:00 2001 From: Asher Date: Tue, 23 Jan 2024 17:10:00 -0900 Subject: [PATCH 2/8] Check indexes in dbmem Might have no match, and a -1 will panic. --- coderd/database/dbmem/dbmem.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 95287b50bd48a..c75070f213f96 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -1310,7 +1310,8 @@ func (q *FakeQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(_ context.Cont keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool { return key.ID == token.APIKeyID }) - if q.oauth2ProviderAppSecrets[secretIdx].AppID == arg.AppID && q.apiKeys[keyIdx].UserID == arg.UserID { + if secretIdx != -1 && q.oauth2ProviderAppSecrets[secretIdx].AppID == arg.AppID && + keyIdx != -1 && q.apiKeys[keyIdx].UserID == arg.UserID { keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID) } else { tokens = append(tokens, token) @@ -2312,7 +2313,7 @@ func (q *FakeQuerier) GetOAuth2ProviderAppsByUserID(_ context.Context, userID uu keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool { return key.ID == token.APIKeyID }) - if q.apiKeys[keyIdx].UserID == userID { + if keyIdx != -1 && q.apiKeys[keyIdx].UserID == userID { tokens = append(tokens, token) } } From b0df9650f8360611f1d0a1ea5f7fc85ed34f2669 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 24 Jan 2024 09:41:15 -0600 Subject: [PATCH 3/8] minor dbauthz changes --- coderd/database/dbauthz/dbauthz.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index a9abce9712ae5..dadf0a61d2f1a 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -826,7 +826,7 @@ func (q *querier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.U if err != nil { return err } - if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(code.UserID.String())); err != nil { + if err := q.authorizeContext(ctx, rbac.ActionDelete, code); err != nil { return err } return q.db.DeleteOAuth2ProviderAppCodeByID(ctx, id) @@ -1222,7 +1222,7 @@ func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2P } func (q *querier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) { - // These two authz checks make sure the caller can read all their own tokens. + // This authz check is to make sure the caller can read all their own tokens. if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(userID.String())); err != nil { return []database.GetOAuth2ProviderAppsByUserIDRow{}, err From cc366f6bbf9690bc8577e7393a6bb8fd5e0843ff Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Wed, 24 Jan 2024 09:43:28 -0600 Subject: [PATCH 4/8] use model struct over rbac.ResourceXX --- coderd/database/dbauthz/dbauthz_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 79a22081b0bae..37b68e68f8841 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2337,7 +2337,7 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() { AppID: app.ID, UserID: user.ID, }) - check.Args(code.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns(code) + check.Args(code.ID).Asserts(code, rbac.ActionRead).Returns(code) })) s.Run("GetOAuth2ProviderAppCodeByAppIDAndSecret", s.Subtest(func(db database.Store, check *expects) { user := dbgen.User(s.T(), db, database.User{}) @@ -2349,7 +2349,7 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() { check.Args(database.GetOAuth2ProviderAppCodeByAppIDAndSecretParams{ AppID: app.ID, HashedSecret: code.HashedSecret, - }).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns(code) + }).Asserts(code, rbac.ActionRead).Returns(code) })) s.Run("InsertOAuth2ProviderAppCode", s.Subtest(func(db database.Store, check *expects) { user := dbgen.User(s.T(), db, database.User{}) @@ -2366,7 +2366,7 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() { AppID: app.ID, UserID: user.ID, }) - check.Args(code.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete) + check.Args(code.ID).Asserts(code, rbac.ActionDelete) })) s.Run("DeleteOAuth2ProviderAppCodesByAppAndUserID", s.Subtest(func(db database.Store, check *expects) { user := dbgen.User(s.T(), db, database.User{}) From be2ad5753cde4da70d0d2f97346a4da5e8b6e549 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 24 Jan 2024 11:12:40 -0900 Subject: [PATCH 5/8] Rename token hashed_secret to refresh_hash --- coderd/database/dbgen/dbgen.go | 12 +- coderd/database/dbmem/dbmem.go | 12 +- coderd/database/dump.sql | 6 +- .../000186_oauth2_provider_codes.up.sql | 6 +- coderd/database/models.go | 14 +- coderd/database/queries.sql.go | 20 +-- coderd/database/queries/oauth2.sql | 2 +- coderd/database/unique_constraint.go | 150 +++++++++--------- 8 files changed, 111 insertions(+), 111 deletions(-) diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index accc593712658..1908b157d1365 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -719,12 +719,12 @@ func OAuth2ProviderAppCode(t testing.TB, db database.Store, seed database.OAuth2 func OAuth2ProviderAppToken(t testing.TB, db database.Store, seed database.OAuth2ProviderAppToken) database.OAuth2ProviderAppToken { token, err := db.InsertOAuth2ProviderAppToken(genCtx, database.InsertOAuth2ProviderAppTokenParams{ - ID: takeFirst(seed.ID, uuid.New()), - CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), - ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()), - HashedSecret: takeFirstSlice(seed.HashedSecret, []byte("hashed-secret")), - AppSecretID: takeFirst(seed.AppSecretID, uuid.New()), - APIKeyID: takeFirst(seed.APIKeyID, uuid.New().String()), + ID: takeFirst(seed.ID, uuid.New()), + CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()), + ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()), + RefreshHash: takeFirstSlice(seed.RefreshHash, []byte("hashed-secret")), + AppSecretID: takeFirst(seed.AppSecretID, uuid.New()), + APIKeyID: takeFirst(seed.APIKeyID, uuid.New().String()), }) require.NoError(t, err, "insert oauth2 app token") return token diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index c75070f213f96..6502af4fd0acb 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -5372,12 +5372,12 @@ func (q *FakeQuerier) InsertOAuth2ProviderAppToken(_ context.Context, arg databa if secret.ID == arg.AppSecretID { //nolint:gosimple // Go wants database.OAuth2ProviderAppToken(arg), but we cannot be sure the structs will remain identical. token := database.OAuth2ProviderAppToken{ - ID: arg.ID, - CreatedAt: arg.CreatedAt, - ExpiresAt: arg.ExpiresAt, - HashedSecret: arg.HashedSecret, - APIKeyID: arg.APIKeyID, - AppSecretID: arg.AppSecretID, + ID: arg.ID, + CreatedAt: arg.CreatedAt, + ExpiresAt: arg.ExpiresAt, + RefreshHash: arg.RefreshHash, + APIKeyID: arg.APIKeyID, + AppSecretID: arg.AppSecretID, } q.oauth2ProviderAppTokens = append(q.oauth2ProviderAppTokens, token) return token, nil diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index f29ae4b47a83f..ba229b6422eb3 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -496,12 +496,12 @@ CREATE TABLE oauth2_provider_app_tokens ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, expires_at timestamp with time zone NOT NULL, - hashed_secret bytea NOT NULL, + refresh_hash bytea NOT NULL, app_secret_id uuid NOT NULL, api_key_id text NOT NULL ); -COMMENT ON TABLE oauth2_provider_app_tokens IS 'Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them.'; +COMMENT ON COLUMN oauth2_provider_app_tokens.refresh_hash IS 'Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key.'; CREATE TABLE oauth2_provider_apps ( id uuid NOT NULL, @@ -1342,7 +1342,7 @@ ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); ALTER TABLE ONLY oauth2_provider_app_tokens - ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_hashed_secret_key UNIQUE (app_secret_id, hashed_secret); + ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_refresh_hash_key UNIQUE (app_secret_id, refresh_hash); ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id); diff --git a/coderd/database/migrations/000186_oauth2_provider_codes.up.sql b/coderd/database/migrations/000186_oauth2_provider_codes.up.sql index 48295361a0238..f3c5f2d2131cf 100644 --- a/coderd/database/migrations/000186_oauth2_provider_codes.up.sql +++ b/coderd/database/migrations/000186_oauth2_provider_codes.up.sql @@ -15,14 +15,14 @@ CREATE TABLE oauth2_provider_app_tokens ( id uuid NOT NULL, created_at timestamp with time zone NOT NULL, expires_at timestamp with time zone NOT NULL, - hashed_secret bytea NOT NULL, + refresh_hash bytea NOT NULL, app_secret_id uuid NOT NULL REFERENCES oauth2_provider_app_secrets (id) ON DELETE CASCADE, api_key_id text NOT NULL REFERENCES api_keys (id) ON DELETE CASCADE, PRIMARY KEY (id), - UNIQUE(app_secret_id, hashed_secret) + UNIQUE(app_secret_id, refresh_hash) ); -COMMENT ON TABLE oauth2_provider_app_tokens IS 'Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them.'; +COMMENT ON COLUMN oauth2_provider_app_tokens.refresh_hash IS 'Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key.'; -- When we delete a token, delete the API key associated with it. CREATE FUNCTION delete_deleted_oauth2_provider_app_token_api_key() RETURNS trigger diff --git a/coderd/database/models.go b/coderd/database/models.go index 29ec66275e875..d84f8d679280b 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1821,14 +1821,14 @@ type OAuth2ProviderAppSecret struct { AppID uuid.UUID `db:"app_id" json:"app_id"` } -// Refresh tokens both provide a way to refresh an access tokens (API keys) and a way to link API keys with the OAuth2 app and secret that generated them. type OAuth2ProviderAppToken struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - ExpiresAt time.Time `db:"expires_at" json:"expires_at"` - HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` - AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` - APIKeyID string `db:"api_key_id" json:"api_key_id"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + // Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key. + RefreshHash []byte `db:"refresh_hash" json:"refresh_hash"` + AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` + APIKeyID string `db:"api_key_id" json:"api_key_id"` } type Organization struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 4741c3a1c7803..6644e08dacf77 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -3035,7 +3035,7 @@ INSERT INTO oauth2_provider_app_tokens ( id, created_at, expires_at, - hashed_secret, + refresh_hash, app_secret_id, api_key_id ) VALUES( @@ -3045,16 +3045,16 @@ INSERT INTO oauth2_provider_app_tokens ( $4, $5, $6 -) RETURNING id, created_at, expires_at, hashed_secret, app_secret_id, api_key_id +) RETURNING id, created_at, expires_at, refresh_hash, app_secret_id, api_key_id ` type InsertOAuth2ProviderAppTokenParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - ExpiresAt time.Time `db:"expires_at" json:"expires_at"` - HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"` - AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` - APIKeyID string `db:"api_key_id" json:"api_key_id"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + ExpiresAt time.Time `db:"expires_at" json:"expires_at"` + RefreshHash []byte `db:"refresh_hash" json:"refresh_hash"` + AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"` + APIKeyID string `db:"api_key_id" json:"api_key_id"` } func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error) { @@ -3062,7 +3062,7 @@ func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg Inser arg.ID, arg.CreatedAt, arg.ExpiresAt, - arg.HashedSecret, + arg.RefreshHash, arg.AppSecretID, arg.APIKeyID, ) @@ -3071,7 +3071,7 @@ func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg Inser &i.ID, &i.CreatedAt, &i.ExpiresAt, - &i.HashedSecret, + &i.RefreshHash, &i.AppSecretID, &i.APIKeyID, ) diff --git a/coderd/database/queries/oauth2.sql b/coderd/database/queries/oauth2.sql index c0c0c7dab6928..f2eb2c9db5781 100644 --- a/coderd/database/queries/oauth2.sql +++ b/coderd/database/queries/oauth2.sql @@ -98,7 +98,7 @@ INSERT INTO oauth2_provider_app_tokens ( id, created_at, expires_at, - hashed_secret, + refresh_hash, app_secret_id, api_key_id ) VALUES( diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index 039172c012a35..2a3c794db7418 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -6,79 +6,79 @@ type UniqueConstraint string // UniqueConstraint enums. 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); - 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); - UniqueDbcryptKeysRevokedKeyDigestKey UniqueConstraint = "dbcrypt_keys_revoked_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_revoked_key_digest_key UNIQUE (revoked_key_digest); - UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); - UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); - UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); - UniqueGitSSHKeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); - UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); - UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); - UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); - UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt); - UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppCodesAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_codes_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); - UniqueOauth2ProviderAppCodesPkey UniqueConstraint = "oauth2_provider_app_codes_pkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppSecretsAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_secrets_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); - UniqueOauth2ProviderAppSecretsPkey UniqueConstraint = "oauth2_provider_app_secrets_pkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppTokensAppSecretIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_tokens_app_secret_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_hashed_secret_key UNIQUE (app_secret_id, hashed_secret); - UniqueOauth2ProviderAppTokensPkey UniqueConstraint = "oauth2_provider_app_tokens_pkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id); - UniqueOauth2ProviderAppsNameKey UniqueConstraint = "oauth2_provider_apps_name_key" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name); - UniqueOauth2ProviderAppsPkey UniqueConstraint = "oauth2_provider_apps_pkey" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_pkey PRIMARY KEY (id); - UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id); - UniqueOrganizationsPkey UniqueConstraint = "organizations_pkey" // ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); - UniqueParameterSchemasJobIDNameKey UniqueConstraint = "parameter_schemas_job_id_name_key" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); - UniqueParameterSchemasPkey UniqueConstraint = "parameter_schemas_pkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id); - UniqueParameterValuesPkey UniqueConstraint = "parameter_values_pkey" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id); - UniqueParameterValuesScopeIDNameKey UniqueConstraint = "parameter_values_scope_id_name_key" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name); - UniqueProvisionerDaemonsPkey UniqueConstraint = "provisioner_daemons_pkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id); - UniqueProvisionerJobLogsPkey UniqueConstraint = "provisioner_job_logs_pkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id); - UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); - UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); - UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetClientSubscriptionsPkey UniqueConstraint = "tailnet_client_subscriptions_pkey" // ALTER TABLE ONLY tailnet_client_subscriptions ADD CONSTRAINT tailnet_client_subscriptions_pkey PRIMARY KEY (client_id, coordinator_id, agent_id); - UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); - UniqueTailnetPeersPkey UniqueConstraint = "tailnet_peers_pkey" // ALTER TABLE ONLY tailnet_peers ADD CONSTRAINT tailnet_peers_pkey PRIMARY KEY (id, coordinator_id); - UniqueTailnetTunnelsPkey UniqueConstraint = "tailnet_tunnels_pkey" // ALTER TABLE ONLY tailnet_tunnels ADD CONSTRAINT tailnet_tunnels_pkey PRIMARY KEY (coordinator_id, src_id, dst_id); - UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); - UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); - UniqueTemplateVersionsPkey UniqueConstraint = "template_versions_pkey" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_pkey PRIMARY KEY (id); - UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); - UniqueTemplatesPkey UniqueConstraint = "templates_pkey" // ALTER TABLE ONLY templates ADD CONSTRAINT templates_pkey PRIMARY KEY (id); - UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type); - UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); - UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); - UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); - UniqueWorkspaceAgentStartupLogsPkey UniqueConstraint = "workspace_agent_startup_logs_pkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id); - UniqueWorkspaceAgentsPkey UniqueConstraint = "workspace_agents_pkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id); - UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id); - UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); - UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); - UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id); - UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); - UniqueWorkspaceBuildsJobIDKey UniqueConstraint = "workspace_builds_job_id_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id); - UniqueWorkspaceBuildsPkey UniqueConstraint = "workspace_builds_pkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id); - UniqueWorkspaceBuildsWorkspaceIDBuildNumberKey UniqueConstraint = "workspace_builds_workspace_id_build_number_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_build_number_key UNIQUE (workspace_id, build_number); - UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); - UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); - UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); - UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); - UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); - UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); - UniqueIndexAPIKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); - UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); - UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); - UniqueIndexProvisionerDaemonsNameOwnerKey UniqueConstraint = "idx_provisioner_daemons_name_owner_key" // CREATE UNIQUE INDEX idx_provisioner_daemons_name_owner_key ON provisioner_daemons USING btree (name, lower(COALESCE((tags ->> 'owner'::text), ''::text))); - UniqueIndexUsersEmail UniqueConstraint = "idx_users_email" // CREATE UNIQUE INDEX idx_users_email ON users USING btree (email) WHERE (deleted = false); - UniqueIndexUsersUsername UniqueConstraint = "idx_users_username" // CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (deleted = false); - UniqueTemplatesOrganizationIDNameIndex UniqueConstraint = "templates_organization_id_name_idx" // CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree (organization_id, lower((name)::text)) WHERE (deleted = false); - UniqueUsersEmailLowerIndex UniqueConstraint = "users_email_lower_idx" // CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false); - UniqueUsersUsernameLowerIndex UniqueConstraint = "users_username_lower_idx" // CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false); - UniqueWorkspaceProxiesLowerNameIndex UniqueConstraint = "workspace_proxies_lower_name_idx" // CREATE UNIQUE INDEX workspace_proxies_lower_name_idx ON workspace_proxies USING btree (lower(name)) WHERE (deleted = false); - UniqueWorkspacesOwnerIDLowerIndex UniqueConstraint = "workspaces_owner_id_lower_idx" // CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false); + 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); + 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); + UniqueDbcryptKeysRevokedKeyDigestKey UniqueConstraint = "dbcrypt_keys_revoked_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_revoked_key_digest_key UNIQUE (revoked_key_digest); + UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); + UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); + UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY external_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); + UniqueGitSSHKeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); + UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); + UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); + UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); + UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt); + UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppCodesAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_codes_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); + UniqueOauth2ProviderAppCodesPkey UniqueConstraint = "oauth2_provider_app_codes_pkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppSecretsAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_secrets_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret); + UniqueOauth2ProviderAppSecretsPkey UniqueConstraint = "oauth2_provider_app_secrets_pkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppTokensAppSecretIDRefreshHashKey UniqueConstraint = "oauth2_provider_app_tokens_app_secret_id_refresh_hash_key" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_refresh_hash_key UNIQUE (app_secret_id, refresh_hash); + UniqueOauth2ProviderAppTokensPkey UniqueConstraint = "oauth2_provider_app_tokens_pkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id); + UniqueOauth2ProviderAppsNameKey UniqueConstraint = "oauth2_provider_apps_name_key" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name); + UniqueOauth2ProviderAppsPkey UniqueConstraint = "oauth2_provider_apps_pkey" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_pkey PRIMARY KEY (id); + UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id); + UniqueOrganizationsPkey UniqueConstraint = "organizations_pkey" // ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); + UniqueParameterSchemasJobIDNameKey UniqueConstraint = "parameter_schemas_job_id_name_key" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); + UniqueParameterSchemasPkey UniqueConstraint = "parameter_schemas_pkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id); + UniqueParameterValuesPkey UniqueConstraint = "parameter_values_pkey" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id); + UniqueParameterValuesScopeIDNameKey UniqueConstraint = "parameter_values_scope_id_name_key" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name); + UniqueProvisionerDaemonsPkey UniqueConstraint = "provisioner_daemons_pkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id); + UniqueProvisionerJobLogsPkey UniqueConstraint = "provisioner_job_logs_pkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id); + UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); + UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); + UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetClientSubscriptionsPkey UniqueConstraint = "tailnet_client_subscriptions_pkey" // ALTER TABLE ONLY tailnet_client_subscriptions ADD CONSTRAINT tailnet_client_subscriptions_pkey PRIMARY KEY (client_id, coordinator_id, agent_id); + UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); + UniqueTailnetPeersPkey UniqueConstraint = "tailnet_peers_pkey" // ALTER TABLE ONLY tailnet_peers ADD CONSTRAINT tailnet_peers_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetTunnelsPkey UniqueConstraint = "tailnet_tunnels_pkey" // ALTER TABLE ONLY tailnet_tunnels ADD CONSTRAINT tailnet_tunnels_pkey PRIMARY KEY (coordinator_id, src_id, dst_id); + UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); + UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); + UniqueTemplateVersionsPkey UniqueConstraint = "template_versions_pkey" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_pkey PRIMARY KEY (id); + UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); + UniqueTemplatesPkey UniqueConstraint = "templates_pkey" // ALTER TABLE ONLY templates ADD CONSTRAINT templates_pkey PRIMARY KEY (id); + UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type); + UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); + UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); + UniqueWorkspaceAgentStartupLogsPkey UniqueConstraint = "workspace_agent_startup_logs_pkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentsPkey UniqueConstraint = "workspace_agents_pkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id); + UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id); + UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); + UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); + UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id); + UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); + UniqueWorkspaceBuildsJobIDKey UniqueConstraint = "workspace_builds_job_id_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id); + UniqueWorkspaceBuildsPkey UniqueConstraint = "workspace_builds_pkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id); + UniqueWorkspaceBuildsWorkspaceIDBuildNumberKey UniqueConstraint = "workspace_builds_workspace_id_build_number_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_build_number_key UNIQUE (workspace_id, build_number); + UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); + UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); + UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); + UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); + UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); + UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); + UniqueIndexAPIKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); + UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); + UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); + UniqueIndexProvisionerDaemonsNameOwnerKey UniqueConstraint = "idx_provisioner_daemons_name_owner_key" // CREATE UNIQUE INDEX idx_provisioner_daemons_name_owner_key ON provisioner_daemons USING btree (name, lower(COALESCE((tags ->> 'owner'::text), ''::text))); + UniqueIndexUsersEmail UniqueConstraint = "idx_users_email" // CREATE UNIQUE INDEX idx_users_email ON users USING btree (email) WHERE (deleted = false); + UniqueIndexUsersUsername UniqueConstraint = "idx_users_username" // CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (deleted = false); + UniqueTemplatesOrganizationIDNameIndex UniqueConstraint = "templates_organization_id_name_idx" // CREATE UNIQUE INDEX templates_organization_id_name_idx ON templates USING btree (organization_id, lower((name)::text)) WHERE (deleted = false); + UniqueUsersEmailLowerIndex UniqueConstraint = "users_email_lower_idx" // CREATE UNIQUE INDEX users_email_lower_idx ON users USING btree (lower(email)) WHERE (deleted = false); + UniqueUsersUsernameLowerIndex UniqueConstraint = "users_username_lower_idx" // CREATE UNIQUE INDEX users_username_lower_idx ON users USING btree (lower(username)) WHERE (deleted = false); + UniqueWorkspaceProxiesLowerNameIndex UniqueConstraint = "workspace_proxies_lower_name_idx" // CREATE UNIQUE INDEX workspace_proxies_lower_name_idx ON workspace_proxies USING btree (lower(name)) WHERE (deleted = false); + UniqueWorkspacesOwnerIDLowerIndex UniqueConstraint = "workspaces_owner_id_lower_idx" // CREATE UNIQUE INDEX workspaces_owner_id_lower_idx ON workspaces USING btree (owner_id, lower((name)::text)) WHERE (deleted = false); ) From 836854810cccb2da7e62f0a22afa6e6283d5d3b3 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 24 Jan 2024 11:15:04 -0900 Subject: [PATCH 6/8] Bump migration number 186 exists in main now. --- ...vider_codes.down.sql => 000187_oauth2_provider_codes.down.sql} | 0 ..._provider_codes.up.sql => 000187_oauth2_provider_codes.up.sql} | 0 ..._provider_codes.up.sql => 000187_oauth2_provider_codes.up.sql} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000186_oauth2_provider_codes.down.sql => 000187_oauth2_provider_codes.down.sql} (100%) rename coderd/database/migrations/{000186_oauth2_provider_codes.up.sql => 000187_oauth2_provider_codes.up.sql} (100%) rename coderd/database/migrations/testdata/fixtures/{000186_oauth2_provider_codes.up.sql => 000187_oauth2_provider_codes.up.sql} (100%) diff --git a/coderd/database/migrations/000186_oauth2_provider_codes.down.sql b/coderd/database/migrations/000187_oauth2_provider_codes.down.sql similarity index 100% rename from coderd/database/migrations/000186_oauth2_provider_codes.down.sql rename to coderd/database/migrations/000187_oauth2_provider_codes.down.sql diff --git a/coderd/database/migrations/000186_oauth2_provider_codes.up.sql b/coderd/database/migrations/000187_oauth2_provider_codes.up.sql similarity index 100% rename from coderd/database/migrations/000186_oauth2_provider_codes.up.sql rename to coderd/database/migrations/000187_oauth2_provider_codes.up.sql diff --git a/coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql b/coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql similarity index 100% rename from coderd/database/migrations/testdata/fixtures/000186_oauth2_provider_codes.up.sql rename to coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql From 29a9e26a761c6e1c601f36e6e30c760e69ff2c87 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 24 Jan 2024 11:17:26 -0900 Subject: [PATCH 7/8] Format oauth2.sql --- coderd/database/queries.sql.go | 15 ++++++++++----- coderd/database/queries/oauth2.sql | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 6644e08dacf77..0e8fb2bade966 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -2653,8 +2653,10 @@ func (q *sqlQuerier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id u } const deleteOAuth2ProviderAppTokensByAppAndUserID = `-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec -DELETE FROM oauth2_provider_app_tokens -USING oauth2_provider_app_secrets, api_keys +DELETE FROM + oauth2_provider_app_tokens +USING + oauth2_provider_app_secrets, api_keys WHERE oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id AND api_keys.id = oauth2_provider_app_tokens.api_key_id @@ -2841,7 +2843,8 @@ func (q *sqlQuerier) GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2Provide } const getOAuth2ProviderAppsByUserID = `-- name: GetOAuth2ProviderAppsByUserID :many -SELECT COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, +SELECT + COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, oauth2_provider_apps.id, oauth2_provider_apps.created_at, oauth2_provider_apps.updated_at, oauth2_provider_apps.name, oauth2_provider_apps.icon, oauth2_provider_apps.callback_url FROM oauth2_provider_app_tokens INNER JOIN oauth2_provider_app_secrets @@ -2850,8 +2853,10 @@ FROM oauth2_provider_app_tokens ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id INNER JOIN api_keys ON api_keys.id = oauth2_provider_app_tokens.api_key_id -WHERE api_keys.user_id = $1 -GROUP BY oauth2_provider_apps.id +WHERE + api_keys.user_id = $1 +GROUP BY + oauth2_provider_apps.id ` type GetOAuth2ProviderAppsByUserIDRow struct { diff --git a/coderd/database/queries/oauth2.sql b/coderd/database/queries/oauth2.sql index f2eb2c9db5781..cf4d7d0f9f643 100644 --- a/coderd/database/queries/oauth2.sql +++ b/coderd/database/queries/oauth2.sql @@ -111,7 +111,8 @@ INSERT INTO oauth2_provider_app_tokens ( ) RETURNING *; -- name: GetOAuth2ProviderAppsByUserID :many -SELECT COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, +SELECT + COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count, sqlc.embed(oauth2_provider_apps) FROM oauth2_provider_app_tokens INNER JOIN oauth2_provider_app_secrets @@ -120,12 +121,16 @@ FROM oauth2_provider_app_tokens ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id INNER JOIN api_keys ON api_keys.id = oauth2_provider_app_tokens.api_key_id -WHERE api_keys.user_id = $1 -GROUP BY oauth2_provider_apps.id; +WHERE + api_keys.user_id = $1 +GROUP BY + oauth2_provider_apps.id; -- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec -DELETE FROM oauth2_provider_app_tokens -USING oauth2_provider_app_secrets, api_keys +DELETE FROM + oauth2_provider_app_tokens +USING + oauth2_provider_app_secrets, api_keys WHERE oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id AND api_keys.id = oauth2_provider_app_tokens.api_key_id From c15f851678406a9ae959eb5ad2c79ac9a93cf701 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 24 Jan 2024 11:46:36 -0900 Subject: [PATCH 8/8] fixup! Rename token hashed_secret to refresh_hash --- .../testdata/fixtures/000187_oauth2_provider_codes.up.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql b/coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql index f4532a4523c6d..92294c76612fc 100644 --- a/coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql +++ b/coderd/database/migrations/testdata/fixtures/000187_oauth2_provider_codes.up.sql @@ -10,7 +10,7 @@ VALUES ( ); INSERT INTO oauth2_provider_app_tokens - (id, created_at, expires_at, hashed_secret, app_secret_id, api_key_id) + (id, created_at, expires_at, refresh_hash, app_secret_id, api_key_id) VALUES ( 'd0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11', '2023-06-15 10:25:33+00', pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy