From ef12fbb7b79d00e1a77da24081aafba33f7c2046 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 14 Dec 2023 15:29:59 -0600 Subject: [PATCH 01/18] chore: enforcement of dbauthz tests was broken --- coderd/database/dbauthz/setup_test.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 33eccfe09ff3b..1d71e361de3f2 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -28,9 +28,10 @@ import ( ) var skipMethods = map[string]string{ - "InTx": "Not relevant", - "Ping": "Not relevant", - "Wrappers": "Not relevant", + "InTx": "Not relevant", + "Ping": "Not relevant", + "Wrappers": "Not relevant", + "AquireLock": "Not relevant", } // TestMethodTestSuite runs MethodTestSuite. @@ -62,7 +63,8 @@ func (s *MethodTestSuite) SetupSuite() { mockStore.EXPECT().Wrappers().Return([]string{}).AnyTimes() az := dbauthz.New(mockStore, nil, slog.Make(), coderdtest.AccessControlStorePointer()) // Take the underlying type of the interface. - azt := reflect.TypeOf(az).Elem() + azt := reflect.TypeOf(az) + require.Greater(s.T(), azt.NumMethod(), 0, "no methods found on querier") s.methodAccounting = make(map[string]int) for i := 0; i < azt.NumMethod(); i++ { method := azt.Method(i) From 15e91ff91699f8c595194f19dd4860429015f8f4 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 14 Dec 2023 15:32:27 -0600 Subject: [PATCH 02/18] Add another skip --- coderd/database/dbauthz/setup_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 1d71e361de3f2..59bf3ff9181ac 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -28,10 +28,11 @@ import ( ) var skipMethods = map[string]string{ - "InTx": "Not relevant", - "Ping": "Not relevant", - "Wrappers": "Not relevant", - "AquireLock": "Not relevant", + "InTx": "Not relevant", + "Ping": "Not relevant", + "Wrappers": "Not relevant", + "AquireLock": "Not relevant", + "TryAcquireLock": "Not relevant", } // TestMethodTestSuite runs MethodTestSuite. From a876975e3eb9451574c592d92e2cefad49d5c575 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 15 Dec 2023 11:43:57 +0000 Subject: [PATCH 03/18] dbauthz: allow asserting errors in tests --- coderd/database/dbauthz/setup_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 59bf3ff9181ac..24c9964931302 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -171,7 +171,11 @@ func (s *MethodTestSuite) Subtest(testCaseF func(db database.Store, check *expec fakeAuthorizer.AlwaysReturn = nil outputs, err := callMethod(ctx) - s.NoError(err, "method %q returned an error", methodName) + if testCase.err == nil { + s.NoError(err, "method %q returned an error", methodName) + } else { + s.EqualError(err, testCase.err.Error(), "method %q returned an unexpected error", methodName) + } // Some tests may not care about the outputs, so we only assert if // they are provided. @@ -292,6 +296,7 @@ type expects struct { assertions []AssertRBAC // outputs is optional. Can assert non-error return values. outputs []reflect.Value + err error } // Asserts is required. Asserts the RBAC authorize calls that should be made. @@ -316,6 +321,12 @@ func (m *expects) Returns(rets ...any) *expects { return m } +// Errors is optional. If it is never called, it will not be asserted. +func (m *expects) Errors(err error) *expects { + m.err = err + return m +} + // AssertRBAC contains the object and actions to be asserted. type AssertRBAC struct { Object rbac.Object From f38be5d75450a836260ce3f54e735321dc7763f9 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 15 Dec 2023 11:44:03 +0000 Subject: [PATCH 04/18] add tailnet methods --- coderd/database/dbauthz/dbauthz_test.go | 138 ++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 3e42ec46ac2fd..031e82f8fc0de 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1419,6 +1419,144 @@ func (s *MethodTestSuite) TestExtraMethods() { })) } +// All functions in this method test suite are not implemented in dbmem, but +// we still want to assert RBAC checks. +func (s *MethodTestSuite) TestTailnetFunctions() { + s.Run("CleanTailnetCoordinators", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("CleanTailnetLostPeers", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("CleanTailnetTunnels", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteAllTailnetClientSubscriptions", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteAllTailnetClientSubscriptionsParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteAllTailnetTunnels", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteAllTailnetTunnelsParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteCoordinator", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteTailnetAgent", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteTailnetAgentParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteTailnetClient", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteTailnetClientParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteTailnetClientSubscription", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteTailnetClientSubscriptionParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteTailnetPeer", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteTailnetPeerParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("DeleteTailnetTunnel", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.DeleteTailnetTunnelParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionDelete). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetAllTailnetAgents", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetTailnetAgents", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetTailnetClientsForAgent", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetTailnetPeers", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetTailnetTunnelPeerBindings", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetTailnetTunnelPeerIDs", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetAllTailnetCoordinators", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetAllTailnetPeers", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("GetAllTailnetTunnels", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionRead). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("UpsertTailnetAgent", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpsertTailnetAgentParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("UpsertTailnetClient", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpsertTailnetClientParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). + Errors(dbmem.ErrUnimplemented) + + })) + s.Run("UpsertTailnetClientSubscription", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpsertTailnetClientSubscriptionParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("UpsertTailnetCoordinator", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("UpsertTailnetPeer", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpsertTailnetPeerParams{ + Status: database.TailnetStatusOk, + }). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionCreate). + Errors(dbmem.ErrUnimplemented) + })) + s.Run("UpsertTailnetTunnel", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpsertTailnetTunnelParams{}). + Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionCreate). + Errors(dbmem.ErrUnimplemented) + })) +} + func (s *MethodTestSuite) TestSystemFunctions() { s.Run("UpdateUserLinkedID", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) From 193d3b3c7027c2a2148bc17064449c186c253bd9 Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 15 Dec 2023 11:48:24 +0000 Subject: [PATCH 05/18] add dbcrypt methods --- coderd/database/dbauthz/dbauthz_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 031e82f8fc0de..767e0fe50ab6e 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1557,6 +1557,28 @@ func (s *MethodTestSuite) TestTailnetFunctions() { })) } +func (s *MethodTestSuite) TestDBCrypt() { + s.Run("GetDBCryptKeys", s.Subtest(func(db database.Store, check *expects) { + check.Args(). + Asserts(rbac.ResourceSystem, rbac.ActionRead). + Returns([]database.DBCryptKey{}) + })) + s.Run("InsertDBCryptKey", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertDBCryptKeyParams{}). + Asserts(rbac.ResourceSystem, rbac.ActionCreate). + Returns() + })) + s.Run("RevokeDBCryptKey", s.Subtest(func(db database.Store, check *expects) { + err := db.InsertDBCryptKey(context.Background(), database.InsertDBCryptKeyParams{ + ActiveKeyDigest: "revoke me", + }) + s.NoError(err) + check.Args("revoke me"). + Asserts(rbac.ResourceSystem, rbac.ActionUpdate). + Returns() + })) +} + func (s *MethodTestSuite) TestSystemFunctions() { s.Run("UpdateUserLinkedID", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) From 2ddae13034a2dafd519bcc7c373425d689b50cca Mon Sep 17 00:00:00 2001 From: Cian Johnston Date: Fri, 15 Dec 2023 11:53:40 +0000 Subject: [PATCH 06/18] fixup! chore: enforcement of dbauthz tests was broken --- coderd/database/dbauthz/setup_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 24c9964931302..dff998aaaa2eb 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -31,7 +31,7 @@ var skipMethods = map[string]string{ "InTx": "Not relevant", "Ping": "Not relevant", "Wrappers": "Not relevant", - "AquireLock": "Not relevant", + "AcquireLock": "Not relevant", "TryAcquireLock": "Not relevant", } From c31b1909982adf9822cd36b17274dfba246a2b19 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 09:39:30 -0600 Subject: [PATCH 07/18] linting --- coderd/database/dbauthz/dbauthz_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 767e0fe50ab6e..c7df19dccd787 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1531,7 +1531,6 @@ func (s *MethodTestSuite) TestTailnetFunctions() { check.Args(database.UpsertTailnetClientParams{}). Asserts(rbac.ResourceTailnetCoordinator, rbac.ActionUpdate). Errors(dbmem.ErrUnimplemented) - })) s.Run("UpsertTailnetClientSubscription", s.Subtest(func(db database.Store, check *expects) { check.Args(database.UpsertTailnetClientSubscriptionParams{}). From e930fb22a03175684faaa8e3bbd4fd8d6f5576fb Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:06:20 -0600 Subject: [PATCH 08/18] Add more dbauthz tests Catch dbmem panic bug --- coderd/database/dbauthz/dbauthz.go | 2 + coderd/database/dbauthz/dbauthz_test.go | 56 +++++++++++++++++++++++++ coderd/database/dbauthz/setup_test.go | 12 +++++- coderd/database/dbmem/dbmem.go | 2 +- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 91722c141ade5..c154b7a89856b 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -2266,10 +2266,12 @@ func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertW } func (q *querier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { + // TODO: This is used by the agent, should we have an rbac check here? return q.db.InsertWorkspaceAgentLogSources(ctx, arg) } func (q *querier) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { + // TODO: This is used by the agent, should we have an rbac check here? return q.db.InsertWorkspaceAgentLogs(ctx, arg) } diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index c7df19dccd787..e8534ba2e0afa 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -225,6 +225,19 @@ func (s *MethodTestSuite) TestAPIKey() { ID: a.ID, }).Asserts(a, rbac.ActionUpdate).Returns() })) + s.Run("DeleteApplicationConnectAPIKeysByUserID", s.Subtest(func(db database.Store, check *expects) { + a, _ := dbgen.APIKey(s.T(), db, database.APIKey{ + Scope: database.APIKeyScopeApplicationConnect, + }) + check.Args(a.UserID).Asserts(rbac.ResourceAPIKey.WithOwner(a.UserID.String()), rbac.ActionDelete).Returns() + })) + s.Run("DeleteExternalAuthLink", s.Subtest(func(db database.Store, check *expects) { + a := dbgen.ExternalAuthLink(s.T(), db, database.ExternalAuthLink{}) + check.Args(database.DeleteExternalAuthLinkParams{ + ProviderID: a.ProviderID, + UserID: a.UserID, + }).Asserts(a, rbac.ActionDelete).Returns() + })) } func (s *MethodTestSuite) TestAuditLogs() { @@ -1048,6 +1061,11 @@ func (s *MethodTestSuite) TestUser() { rbac.ResourceRoleAssignment, rbac.ActionDelete, ).Returns(o) })) + s.Run("AllUserIDs", s.Subtest(func(db database.Store, check *expects) { + a := dbgen.User(s.T(), db, database.User{}) + b := dbgen.User(s.T(), db, database.User{}) + check.Args().Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(slice.New(a.ID, b.ID)) + })) } func (s *MethodTestSuite) TestWorkspace() { @@ -1405,6 +1423,14 @@ func (s *MethodTestSuite) TestWorkspace() { app := dbgen.WorkspaceApp(s.T(), db, database.WorkspaceApp{AgentID: agt.ID}) check.Args(app.ID).Asserts(ws, rbac.ActionRead).Returns(ws) })) + s.Run("ActivityBumpWorkspace", s.Subtest(func(db database.Store, check *expects) { + ws := dbgen.Workspace(s.T(), db, database.Workspace{}) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: build.JobID, Type: database.ProvisionerJobTypeWorkspaceBuild}) + check.Args(database.ActivityBumpWorkspaceParams{ + WorkspaceID: ws.ID, + }).Asserts(ws, rbac.ActionUpdate).Returns() + })) } func (s *MethodTestSuite) TestExtraMethods() { @@ -1417,6 +1443,15 @@ func (s *MethodTestSuite) TestExtraMethods() { s.NoError(err, "insert provisioner daemon") check.Args().Asserts(d, rbac.ActionRead) })) + s.Run("DeleteOldProvisionerDaemons", s.Subtest(func(db database.Store, check *expects) { + _, err := db.UpsertProvisionerDaemon(context.Background(), database.UpsertProvisionerDaemonParams{ + Tags: database.StringMap(map[string]string{ + provisionersdk.TagScope: provisionersdk.ScopeOrganization, + }), + }) + s.NoError(err, "insert provisioner daemon") + check.Args().Asserts(rbac.ResourceSystem, rbac.ActionDelete) + })) } // All functions in this method test suite are not implemented in dbmem, but @@ -1877,4 +1912,25 @@ func (s *MethodTestSuite) TestSystemFunctions() { Transition: database.WorkspaceTransitionStart, }).Asserts(rbac.ResourceSystem, rbac.ActionCreate) })) + s.Run("DeleteOldWorkspaceAgentLogs", s.Subtest(func(db database.Store, check *expects) { + check.Args().Asserts(rbac.ResourceSystem, rbac.ActionDelete) + })) + s.Run("InsertWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAgentStatsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(matchAnyError) + })) + s.Run("InsertWorkspaceAppStats", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAppStatsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + })) + s.Run("InsertWorkspaceAgentScripts", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAgentScriptsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + })) + s.Run("InsertWorkspaceAgentMetadata", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAgentMetadataParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + })) + s.Run("InsertWorkspaceAgentLogs", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAgentLogsParams{}).Asserts() + })) + s.Run("InsertWorkspaceAgentLogSources", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertWorkspaceAgentLogSourcesParams{}).Asserts() + })) } diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index dff998aaaa2eb..928b78e4c3b38 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -2,6 +2,7 @@ package dbauthz_test import ( "context" + "errors" "fmt" "reflect" "sort" @@ -27,6 +28,10 @@ import ( "github.com/coder/coder/v2/coderd/util/slice" ) +var ( + matchAnyError = errors.New("match any error") +) + var skipMethods = map[string]string{ "InTx": "Not relevant", "Ping": "Not relevant", @@ -174,7 +179,12 @@ func (s *MethodTestSuite) Subtest(testCaseF func(db database.Store, check *expec if testCase.err == nil { s.NoError(err, "method %q returned an error", methodName) } else { - s.EqualError(err, testCase.err.Error(), "method %q returned an unexpected error", methodName) + if errors.Is(testCase.err, matchAnyError) { + // This means we do not care exactly what the error is. + s.Error(err, "method %q returned an error", methodName) + } else { + s.EqualError(err, testCase.err.Error(), "method %q returned an unexpected error", methodName) + } } // Some tests may not care about the outputs, so we only assert if diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 2e85bda1ffcbd..95e5528dbba0a 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -880,7 +880,7 @@ func (q *FakeQuerier) AllUserIDs(_ context.Context) ([]uuid.UUID, error) { defer q.mutex.RUnlock() userIDs := make([]uuid.UUID, 0, len(q.users)) for idx := range q.users { - userIDs[idx] = q.users[idx].ID + userIDs = append(userIDs, q.users[idx].ID) } return userIDs, nil } From f6fe76f683a915184a9cac40091355ad4b00c23f Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:20:18 -0600 Subject: [PATCH 09/18] GetTemplateInsights require an actor to call --- coderd/database/dbauthz/dbauthz.go | 8 ++++++-- coderd/database/dbauthz/dbauthz_test.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index c154b7a89856b..7253d8c2a1d99 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -62,6 +62,10 @@ func IsNotAuthorizedError(err error) bool { if err == nil { return false } + if xerrors.Is(err, NoActorError) { + return true + } + return xerrors.As(err, &NotAuthorizedError{}) } @@ -1393,7 +1397,7 @@ func (q *querier) GetTemplateDAUs(ctx context.Context, arg database.GetTemplateD func (q *querier) GetTemplateInsights(ctx context.Context, arg database.GetTemplateInsightsParams) (database.GetTemplateInsightsRow, error) { // Used by TemplateInsights endpoint // For auditors, check read template_insights, and fall back to update template. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { @@ -1416,7 +1420,7 @@ func (q *querier) GetTemplateInsights(ctx context.Context, arg database.GetTempl func (q *querier) GetTemplateInsightsByInterval(ctx context.Context, arg database.GetTemplateInsightsByIntervalParams) ([]database.GetTemplateInsightsByIntervalRow, error) { // Used by TemplateInsights endpoint // For auditors, check read template_insights, and fall back to update template. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index e8534ba2e0afa..bf4f0c32606ed 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -888,6 +888,20 @@ func (s *MethodTestSuite) TestTemplate() { ExternalAuthProviders: []string{}, }).Asserts(t1, rbac.ActionUpdate).Returns() })) + s.Run("GetTemplateInsights", s.Subtest(func(db database.Store, check *expects) { + //tpl := dbgen.Template(s.T(), db, database.Template{}) + check.Args(database.GetTemplateInsightsParams{ + StartTime: time.Time{}, + EndTime: time.Now(), + TemplateIDs: []uuid.UUID{}, + }).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) + s.Run("GetTemplateInsightsByInterval", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateInsightsByIntervalParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) + s.Run("GetTemplateInsightsByTemplate", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateInsightsByTemplateParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) } func (s *MethodTestSuite) TestUser() { @@ -1933,4 +1947,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("InsertWorkspaceAgentLogSources", s.Subtest(func(db database.Store, check *expects) { check.Args(database.InsertWorkspaceAgentLogSourcesParams{}).Asserts() })) + s.Run("GetTemplateDAUs", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateDAUsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) } From 48857d2dfdc6192f9dad15bd71283f2e7f24252d Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:23:44 -0600 Subject: [PATCH 10/18] Add more system functions; --- coderd/database/dbauthz/dbauthz_test.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index bf4f0c32606ed..a245b45a236bd 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1950,4 +1950,25 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetTemplateDAUs", s.Subtest(func(db database.Store, check *expects) { check.Args(database.GetTemplateDAUsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionRead) })) + s.Run("GetActiveWorkspaceBuildsByTemplateID", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetDeploymentDAUs", s.Subtest(func(db database.Store, check *expects) { + check.Args(int32(0)).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetAppSecurityKey", s.Subtest(func(db database.Store, check *expects) { + check.Args().Asserts() + })) + s.Run("UpsertAppSecurityKey", s.Subtest(func(db database.Store, check *expects) { + check.Args("").Asserts() + })) + s.Run("GetApplicationName", s.Subtest(func(db database.Store, check *expects) { + check.Args().Asserts() + })) + s.Run("UpsertApplicationName", s.Subtest(func(db database.Store, check *expects) { + check.Args("").Asserts(rbac.ResourceDeploymentValues, rbac.ActionCreate) + })) + s.Run("GetHealthSettings", s.Subtest(func(db database.Store, check *expects) { + check.Args().Asserts() + })) } From 2e2f6be2064fc6ebd899006741dca771580ee4f2 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:34:07 -0600 Subject: [PATCH 11/18] Implement another chunk --- coderd/database/dbauthz/dbauthz.go | 2 +- coderd/database/dbauthz/dbauthz_test.go | 47 +++++++++++++++++++++---- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 7253d8c2a1d99..c9ba11d323f2c 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1342,7 +1342,7 @@ func (q *querier) GetTailnetTunnelPeerIDs(ctx context.Context, srcID uuid.UUID) func (q *querier) GetTemplateAppInsights(ctx context.Context, arg database.GetTemplateAppInsightsParams) ([]database.GetTemplateAppInsightsRow, error) { // Used by TemplateAppInsights endpoint // For auditors, check read template_insights, and fall back to update template. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index a245b45a236bd..5b8fcd49edbfe 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -238,6 +238,13 @@ func (s *MethodTestSuite) TestAPIKey() { UserID: a.UserID, }).Asserts(a, rbac.ActionDelete).Returns() })) + s.Run("GetExternalAuthLinksByUserID", s.Subtest(func(db database.Store, check *expects) { + a := dbgen.ExternalAuthLink(s.T(), db, database.ExternalAuthLink{}) + b := dbgen.ExternalAuthLink(s.T(), db, database.ExternalAuthLink{ + UserID: a.UserID, + }) + check.Args(a.UserID).Asserts(a, rbac.ActionRead, b, rbac.ActionRead) + })) } func (s *MethodTestSuite) TestAuditLogs() { @@ -890,11 +897,7 @@ func (s *MethodTestSuite) TestTemplate() { })) s.Run("GetTemplateInsights", s.Subtest(func(db database.Store, check *expects) { //tpl := dbgen.Template(s.T(), db, database.Template{}) - check.Args(database.GetTemplateInsightsParams{ - StartTime: time.Time{}, - EndTime: time.Now(), - TemplateIDs: []uuid.UUID{}, - }).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + check.Args(database.GetTemplateInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) })) s.Run("GetTemplateInsightsByInterval", s.Subtest(func(db database.Store, check *expects) { check.Args(database.GetTemplateInsightsByIntervalParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) @@ -902,9 +905,21 @@ func (s *MethodTestSuite) TestTemplate() { s.Run("GetTemplateInsightsByTemplate", s.Subtest(func(db database.Store, check *expects) { check.Args(database.GetTemplateInsightsByTemplateParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) })) + s.Run("GetTemplateAppInsights", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateAppInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) + s.Run("GetTemplateAppInsightsByTemplate", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateAppInsightsByTemplateParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) } func (s *MethodTestSuite) TestUser() { + s.Run("GetAuthorizedUsers", s.Subtest(func(db database.Store, check *expects) { + dbgen.User(s.T(), db, database.User{}) + // No asserts because SQLFilter. + check.Args(database.GetUsersParams{}, emptyPreparedAuthorized{}). + Asserts() + })) s.Run("DeleteAPIKeysByUserID", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) check.Args(u.ID).Asserts(rbac.ResourceAPIKey.WithOwner(u.ID.String()), rbac.ActionDelete).Returns() @@ -1951,7 +1966,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { check.Args(database.GetTemplateDAUsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionRead) })) s.Run("GetActiveWorkspaceBuildsByTemplateID", s.Subtest(func(db database.Store, check *expects) { - check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead) + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead).Errors(sql.ErrNoRows) })) s.Run("GetDeploymentDAUs", s.Subtest(func(db database.Store, check *expects) { check.Args(int32(0)).Asserts(rbac.ResourceSystem, rbac.ActionRead) @@ -1963,6 +1978,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { check.Args("").Asserts() })) s.Run("GetApplicationName", s.Subtest(func(db database.Store, check *expects) { + db.UpsertApplicationName(context.Background(), "foo") check.Args().Asserts() })) s.Run("UpsertApplicationName", s.Subtest(func(db database.Store, check *expects) { @@ -1971,4 +1987,23 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetHealthSettings", s.Subtest(func(db database.Store, check *expects) { check.Args().Asserts() })) + s.Run("GetDeploymentWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { + check.Args(time.Time{}).Asserts() + })) + s.Run("GetDeploymentWorkspaceStats", s.Subtest(func(db database.Store, check *expects) { + check.Args().Asserts() + })) + s.Run("GetFileTemplates", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetHungProvisionerJobs", s.Subtest(func(db database.Store, check *expects) { + check.Args(time.Time{}).Asserts() + })) + s.Run("GetOAuthSigningKey", s.Subtest(func(db database.Store, check *expects) { + db.UpsertOAuthSigningKey(context.Background(), "foo") + check.Args().Asserts(rbac.ResourceSystem, rbac.ActionUpdate) + })) + s.Run("InsertMissingGroups", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertMissingGroupsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(matchAnyError) + })) } From 5d2f9b16945a05aa2ddfe681c952f1e443da74b7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:40:33 -0600 Subject: [PATCH 12/18] Another chunk of tests --- coderd/database/dbauthz/dbauthz_test.go | 47 +++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 5b8fcd49edbfe..47baa3117982e 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -987,6 +987,12 @@ func (s *MethodTestSuite) TestUser() { ID: u.ID, }).Asserts(u.UserDataRBACObject(), rbac.ActionUpdate).Returns() })) + s.Run("UpdateUserQuietHoursSchedule", s.Subtest(func(db database.Store, check *expects) { + u := dbgen.User(s.T(), db, database.User{}) + check.Args(database.UpdateUserQuietHoursScheduleParams{ + ID: u.ID, + }).Asserts(u.UserDataRBACObject(), rbac.ActionUpdate) + })) s.Run("UpdateUserLastSeenAt", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) check.Args(database.UpdateUserLastSeenAtParams{ @@ -1152,6 +1158,18 @@ func (s *MethodTestSuite) TestWorkspace() { LifecycleState: database.WorkspaceAgentLifecycleStateCreated, }).Asserts(ws, rbac.ActionUpdate).Returns() })) + s.Run("UpdateWorkspaceAgentMetadata", s.Subtest(func(db database.Store, check *expects) { + tpl := dbgen.Template(s.T(), db, database.Template{}) + ws := dbgen.Workspace(s.T(), db, database.Workspace{ + TemplateID: tpl.ID, + }) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) + agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) + check.Args(database.UpdateWorkspaceAgentMetadataParams{ + WorkspaceAgentID: agt.ID, + }).Asserts(ws, rbac.ActionUpdate).Returns() + })) s.Run("UpdateWorkspaceAgentLogOverflowByID", s.Subtest(func(db database.Store, check *expects) { tpl := dbgen.Template(s.T(), db, database.Template{}) ws := dbgen.Workspace(s.T(), db, database.Workspace{ @@ -1264,6 +1282,16 @@ func (s *MethodTestSuite) TestWorkspace() { TemplateName: tpl.Name, }) })) + s.Run("GetWorkspaceAgentsInLatestBuildByWorkspaceID", s.Subtest(func(db database.Store, check *expects) { + tpl := dbgen.Template(s.T(), db, database.Template{}) + ws := dbgen.Workspace(s.T(), db, database.Workspace{ + TemplateID: tpl.ID, + }) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) + dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) + check.Args(ws.ID).Asserts(ws, rbac.ActionRead) + })) s.Run("GetWorkspaceByOwnerIDAndName", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) check.Args(database.GetWorkspaceByOwnerIDAndNameParams{ @@ -1987,6 +2015,9 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetHealthSettings", s.Subtest(func(db database.Store, check *expects) { check.Args().Asserts() })) + s.Run("UpsertHealthSettings", s.Subtest(func(db database.Store, check *expects) { + check.Args("foo").Asserts(rbac.ResourceDeploymentValues, rbac.ActionCreate) + })) s.Run("GetDeploymentWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { check.Args(time.Time{}).Asserts() })) @@ -1999,6 +2030,9 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetHungProvisionerJobs", s.Subtest(func(db database.Store, check *expects) { check.Args(time.Time{}).Asserts() })) + s.Run("UpsertOAuthSigningKey", s.Subtest(func(db database.Store, check *expects) { + check.Args("foo").Asserts(rbac.ResourceSystem, rbac.ActionUpdate) + })) s.Run("GetOAuthSigningKey", s.Subtest(func(db database.Store, check *expects) { db.UpsertOAuthSigningKey(context.Background(), "foo") check.Args().Asserts(rbac.ResourceSystem, rbac.ActionUpdate) @@ -2006,4 +2040,17 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("InsertMissingGroups", s.Subtest(func(db database.Store, check *expects) { check.Args(database.InsertMissingGroupsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(matchAnyError) })) + s.Run("UpdateUserLoginType", s.Subtest(func(db database.Store, check *expects) { + u := dbgen.User(s.T(), db, database.User{}) + check.Args(database.UpdateUserLoginTypeParams{ + NewLoginType: database.LoginTypePassword, + UserID: u.ID, + }).Asserts(rbac.ResourceSystem, rbac.ActionUpdate) + })) + s.Run("GetWorkspaceAgentStatsAndLabels", s.Subtest(func(db database.Store, check *expects) { + check.Args(time.Time{}).Asserts() + })) + s.Run("GetWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { + check.Args(time.Time{}).Asserts() + })) } From 64815617047393e24b7412629e24166e4c90c4df Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 10:54:07 -0600 Subject: [PATCH 13/18] Another chunk --- coderd/database/dbauthz/dbauthz.go | 6 ++-- coderd/database/dbauthz/dbauthz_test.go | 44 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index c9ba11d323f2c..95693062767c6 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1451,7 +1451,7 @@ func (q *querier) GetTemplateInsightsByTemplate(ctx context.Context, arg databas func (q *querier) GetTemplateParameterInsights(ctx context.Context, arg database.GetTemplateParameterInsightsParams) ([]database.GetTemplateParameterInsightsRow, error) { // Used by both insights endpoint and prometheus collector. // For auditors, check read template_insights, and fall back to update template. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { @@ -1624,7 +1624,7 @@ func (q *querier) GetUnexpiredLicenses(ctx context.Context) ([]database.License, func (q *querier) GetUserActivityInsights(ctx context.Context, arg database.GetUserActivityInsightsParams) ([]database.GetUserActivityInsightsRow, error) { // Used by insights endpoints. Need to check both for auditors and for regular users with template acl perms. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { @@ -1661,7 +1661,7 @@ func (q *querier) GetUserCount(ctx context.Context) (int64, error) { func (q *querier) GetUserLatencyInsights(ctx context.Context, arg database.GetUserLatencyInsightsParams) ([]database.GetUserLatencyInsightsRow, error) { // Used by insights endpoints. Need to check both for auditors and for regular users with template acl perms. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); IsNotAuthorizedError(err) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights);err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 47baa3117982e..2678b2ec091b0 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -665,6 +665,10 @@ func (s *MethodTestSuite) TestWorkspaceProxy() { p, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) check.Args(p.ID).Asserts(p, rbac.ActionRead).Returns(p) })) + s.Run("GetWorkspaceProxyByName", s.Subtest(func(db database.Store, check *expects) { + p, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) + check.Args(p.Name).Asserts(p, rbac.ActionRead).Returns(p) + })) s.Run("UpdateWorkspaceProxyDeleted", s.Subtest(func(db database.Store, check *expects) { p, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) check.Args(database.UpdateWorkspaceProxyDeletedParams{ @@ -672,6 +676,12 @@ func (s *MethodTestSuite) TestWorkspaceProxy() { Deleted: true, }).Asserts(p, rbac.ActionDelete) })) + s.Run("UpdateWorkspaceProxy", s.Subtest(func(db database.Store, check *expects) { + p, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) + check.Args(database.UpdateWorkspaceProxyParams{ + ID: p.ID, + }).Asserts(p, rbac.ActionUpdate) + })) s.Run("GetWorkspaceProxies", s.Subtest(func(db database.Store, check *expects) { p1, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) p2, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{}) @@ -899,6 +909,15 @@ func (s *MethodTestSuite) TestTemplate() { //tpl := dbgen.Template(s.T(), db, database.Template{}) check.Args(database.GetTemplateInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) })) + s.Run("GetUserLatencyInsights", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetUserLatencyInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) + s.Run("GetUserActivityInsights", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetUserActivityInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) + s.Run("GetTemplateParameterInsights", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateParameterInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) + })) s.Run("GetTemplateInsightsByInterval", s.Subtest(func(db database.Store, check *expects) { check.Args(database.GetTemplateInsightsByIntervalParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) })) @@ -1416,6 +1435,19 @@ func (s *MethodTestSuite) TestWorkspace() { ID: w.ID, }).Asserts(w, rbac.ActionUpdate).Returns(expected) })) + s.Run("UpdateWorkspaceDormantDeletingAt", s.Subtest(func(db database.Store, check *expects) { + w := dbgen.Workspace(s.T(), db, database.Workspace{}) + check.Args(database.UpdateWorkspaceDormantDeletingAtParams{ + ID: w.ID, + }).Asserts(w, rbac.ActionUpdate) + })) + s.Run("UpdateWorkspaceAutomaticUpdates", s.Subtest(func(db database.Store, check *expects) { + w := dbgen.Workspace(s.T(), db, database.Workspace{}) + check.Args(database.UpdateWorkspaceAutomaticUpdatesParams{ + ID: w.ID, + AutomaticUpdates: database.AutomaticUpdatesAlways, + }).Asserts(w, rbac.ActionUpdate) + })) s.Run("InsertWorkspaceAgentStat", s.Subtest(func(db database.Store, check *expects) { ws := dbgen.Workspace(s.T(), db, database.Workspace{}) check.Args(database.InsertWorkspaceAgentStatParams{ @@ -2053,4 +2085,16 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { check.Args(time.Time{}).Asserts() })) + s.Run("GetWorkspaceProxyByHostname", s.Subtest(func(db database.Store, check *expects) { + p, _ := dbgen.WorkspaceProxy(s.T(), db, database.WorkspaceProxy{ + WildcardHostname: "*.example.com", + }) + check.Args(database.GetWorkspaceProxyByHostnameParams{ + Hostname: "foo.example.com", + AllowWildcardHostname: true, + }).Asserts(rbac.ResourceSystem, rbac.ActionRead).Returns(p) + })) + s.Run("GetTemplateAverageBuildTime", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.GetTemplateAverageBuildTimeParams{}).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) } From 55fbd8ffa09056e8c667f57fb7dba9f005e887f1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 11:02:17 -0600 Subject: [PATCH 14/18] Update template tests --- coderd/database/dbauthz/dbauthz_test.go | 33 +++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 2678b2ec091b0..7f1a2ef477a92 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -843,6 +843,30 @@ func (s *MethodTestSuite) TestTemplate() { ID: t1.ID, }).Asserts(t1, rbac.ActionCreate) })) + s.Run("UpdateTemplateAccessControlByID", s.Subtest(func(db database.Store, check *expects) { + t1 := dbgen.Template(s.T(), db, database.Template{}) + check.Args(database.UpdateTemplateAccessControlByIDParams{ + ID: t1.ID, + }).Asserts(t1, rbac.ActionUpdate) + })) + s.Run("UpdateTemplateScheduleByID", s.Subtest(func(db database.Store, check *expects) { + t1 := dbgen.Template(s.T(), db, database.Template{}) + check.Args(database.UpdateTemplateScheduleByIDParams{ + ID: t1.ID, + }).Asserts(t1, rbac.ActionUpdate) + })) + s.Run("UpdateTemplateWorkspacesLastUsedAt", s.Subtest(func(db database.Store, check *expects) { + t1 := dbgen.Template(s.T(), db, database.Template{}) + check.Args(database.UpdateTemplateWorkspacesLastUsedAtParams{ + TemplateID: t1.ID, + }).Asserts(t1, rbac.ActionUpdate) + })) + s.Run("UpdateWorkspacesDormantDeletingAtByTemplateID", s.Subtest(func(db database.Store, check *expects) { + t1 := dbgen.Template(s.T(), db, database.Template{}) + check.Args(database.UpdateWorkspacesDormantDeletingAtByTemplateIDParams{ + TemplateID: t1.ID, + }).Asserts(t1, rbac.ActionUpdate) + })) s.Run("UpdateTemplateActiveVersionByID", s.Subtest(func(db database.Store, check *expects) { t1 := dbgen.Template(s.T(), db, database.Template{ ActiveVersionID: uuid.New(), @@ -2097,4 +2121,13 @@ func (s *MethodTestSuite) TestSystemFunctions() { s.Run("GetTemplateAverageBuildTime", s.Subtest(func(db database.Store, check *expects) { check.Args(database.GetTemplateAverageBuildTimeParams{}).Asserts(rbac.ResourceSystem, rbac.ActionRead) })) + s.Run("GetWorkspacesEligibleForTransition", s.Subtest(func(db database.Store, check *expects) { + check.Args(time.Time{}).Asserts() + })) + s.Run("InsertTemplateVersionVariable", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.InsertTemplateVersionVariableParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + })) + s.Run("UpdateInactiveUsersToDormant", s.Subtest(func(db database.Store, check *expects) { + check.Args(database.UpdateInactiveUsersToDormantParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + })) } From b5ba222cf2fa715082aeac78d33c3a5d0cdc1116 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 11:13:41 -0600 Subject: [PATCH 15/18] Passing tests --- coderd/database/dbauthz/dbauthz_test.go | 60 ++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 7f1a2ef477a92..8bc12f66cfd57 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1178,6 +1178,34 @@ func (s *MethodTestSuite) TestWorkspace() { agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) check.Args(agt.ID).Asserts(ws, rbac.ActionRead).Returns(agt) })) + s.Run("GetWorkspaceAgentLifecycleStateByID", s.Subtest(func(db database.Store, check *expects) { + tpl := dbgen.Template(s.T(), db, database.Template{}) + ws := dbgen.Workspace(s.T(), db, database.Workspace{ + TemplateID: tpl.ID, + }) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) + agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) + check.Args(agt.ID).Asserts(ws, rbac.ActionRead) + })) + s.Run("GetWorkspaceAgentMetadata", s.Subtest(func(db database.Store, check *expects) { + tpl := dbgen.Template(s.T(), db, database.Template{}) + ws := dbgen.Workspace(s.T(), db, database.Workspace{ + TemplateID: tpl.ID, + }) + build := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: ws.ID, JobID: uuid.New()}) + res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID}) + agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID}) + _ = db.InsertWorkspaceAgentMetadata(context.Background(), database.InsertWorkspaceAgentMetadataParams{ + WorkspaceAgentID: agt.ID, + DisplayName: "test", + Key: "test", + }) + check.Args(database.GetWorkspaceAgentMetadataParams{ + WorkspaceAgentID: agt.ID, + Keys: []string{"test"}, + }).Asserts(ws, rbac.ActionRead) + })) s.Run("GetWorkspaceAgentByInstanceID", s.Subtest(func(db database.Store, check *expects) { tpl := dbgen.Template(s.T(), db, database.Template{}) ws := dbgen.Workspace(s.T(), db, database.Workspace{ @@ -1878,6 +1906,15 @@ func (s *MethodTestSuite) TestSystemFunctions() { Asserts(rbac.ResourceSystem, rbac.ActionRead). Returns(slice.New(tv1, tv2, tv3)) })) + s.Run("GetParameterSchemasByJobID", s.Subtest(func(db database.Store, check *expects) { + tpl := dbgen.Template(s.T(), db, database.Template{}) + tv := dbgen.TemplateVersion(s.T(), db, database.TemplateVersion{ + TemplateID: uuid.NullUUID{UUID: tpl.ID, Valid: true}, + }) + job := dbgen.ProvisionerJob(s.T(), db, nil, database.ProvisionerJob{ID: tv.JobID}) + check.Args(job.ID). + Asserts(tpl, rbac.ActionRead).Errors(sql.ErrNoRows) + })) s.Run("GetWorkspaceAppsByAgentIDs", s.Subtest(func(db database.Store, check *expects) { aWs := dbgen.Workspace(s.T(), db, database.Workspace{}) aBuild := dbgen.WorkspaceBuild(s.T(), db, database.WorkspaceBuild{WorkspaceID: aWs.ID, JobID: uuid.New()}) @@ -2128,6 +2165,27 @@ func (s *MethodTestSuite) TestSystemFunctions() { check.Args(database.InsertTemplateVersionVariableParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) })) s.Run("UpdateInactiveUsersToDormant", s.Subtest(func(db database.Store, check *expects) { - check.Args(database.UpdateInactiveUsersToDormantParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) + check.Args(database.UpdateInactiveUsersToDormantParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(sql.ErrNoRows) + })) + s.Run("GetWorkspaceUniqueOwnerCountByTemplateIDs", s.Subtest(func(db database.Store, check *expects) { + check.Args([]uuid.UUID{uuid.New()}).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetWorkspaceAgentScriptsByAgentIDs", s.Subtest(func(db database.Store, check *expects) { + check.Args([]uuid.UUID{uuid.New()}).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetWorkspaceAgentLogSourcesByAgentIDs", s.Subtest(func(db database.Store, check *expects) { + check.Args([]uuid.UUID{uuid.New()}).Asserts(rbac.ResourceSystem, rbac.ActionRead) + })) + s.Run("GetProvisionerJobsByIDsWithQueuePosition", s.Subtest(func(db database.Store, check *expects) { + check.Args([]uuid.UUID{}).Asserts() + })) + s.Run("GetReplicaByID", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead).Errors(sql.ErrNoRows) + })) + s.Run("GetWorkspaceAgentAndOwnerByAuthToken", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead).Errors(sql.ErrNoRows) + })) + s.Run("GetUserLinksByUserID", s.Subtest(func(db database.Store, check *expects) { + check.Args(uuid.New()).Asserts(rbac.ResourceSystem, rbac.ActionRead) })) } From 531c7f8de9241931204e3d6421cf8f3d1953e27c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 11:27:21 -0600 Subject: [PATCH 16/18] Lint --- coderd/database/dbauthz/setup_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index 928b78e4c3b38..a99eb8d0a7bc5 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -28,9 +28,7 @@ import ( "github.com/coder/coder/v2/coderd/util/slice" ) -var ( - matchAnyError = errors.New("match any error") -) +var matchAnyError = errors.New("match any error") var skipMethods = map[string]string{ "InTx": "Not relevant", From 440573aa887895916f4edc2b8620e9a0480fbbf4 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 12:04:39 -0600 Subject: [PATCH 17/18] lint --- coderd/database/dbauthz/dbauthz_test.go | 4 ++-- coderd/database/dbauthz/setup_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 8bc12f66cfd57..53932239125e5 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -2066,7 +2066,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { check.Args().Asserts(rbac.ResourceSystem, rbac.ActionDelete) })) s.Run("InsertWorkspaceAgentStats", s.Subtest(func(db database.Store, check *expects) { - check.Args(database.InsertWorkspaceAgentStatsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(matchAnyError) + check.Args(database.InsertWorkspaceAgentStatsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(errMatchAny) })) s.Run("InsertWorkspaceAppStats", s.Subtest(func(db database.Store, check *expects) { check.Args(database.InsertWorkspaceAppStatsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate) @@ -2131,7 +2131,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { check.Args().Asserts(rbac.ResourceSystem, rbac.ActionUpdate) })) s.Run("InsertMissingGroups", s.Subtest(func(db database.Store, check *expects) { - check.Args(database.InsertMissingGroupsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(matchAnyError) + check.Args(database.InsertMissingGroupsParams{}).Asserts(rbac.ResourceSystem, rbac.ActionCreate).Errors(errMatchAny) })) s.Run("UpdateUserLoginType", s.Subtest(func(db database.Store, check *expects) { u := dbgen.User(s.T(), db, database.User{}) diff --git a/coderd/database/dbauthz/setup_test.go b/coderd/database/dbauthz/setup_test.go index a99eb8d0a7bc5..3c54d8be4e345 100644 --- a/coderd/database/dbauthz/setup_test.go +++ b/coderd/database/dbauthz/setup_test.go @@ -28,7 +28,7 @@ import ( "github.com/coder/coder/v2/coderd/util/slice" ) -var matchAnyError = errors.New("match any error") +var errMatchAny = errors.New("match any error") var skipMethods = map[string]string{ "InTx": "Not relevant", @@ -177,7 +177,7 @@ func (s *MethodTestSuite) Subtest(testCaseF func(db database.Store, check *expec if testCase.err == nil { s.NoError(err, "method %q returned an error", methodName) } else { - if errors.Is(testCase.err, matchAnyError) { + if errors.Is(testCase.err, errMatchAny) { // This means we do not care exactly what the error is. s.Error(err, "method %q returned an error", methodName) } else { From af0b3f5b1080ce150c453c4a00d09233759b340a Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Fri, 15 Dec 2023 12:24:52 -0600 Subject: [PATCH 18/18] fmt --- coderd/database/dbauthz/dbauthz.go | 2 +- coderd/database/dbauthz/dbauthz_test.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 95693062767c6..5ba613044f661 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1661,7 +1661,7 @@ func (q *querier) GetUserCount(ctx context.Context) (int64, error) { func (q *querier) GetUserLatencyInsights(ctx context.Context, arg database.GetUserLatencyInsightsParams) ([]database.GetUserLatencyInsightsRow, error) { // Used by insights endpoints. Need to check both for auditors and for regular users with template acl perms. - if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights);err != nil { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceTemplateInsights); err != nil { for _, templateID := range arg.TemplateIDs { template, err := q.db.GetTemplateByID(ctx, templateID) if err != nil { diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 53932239125e5..5f40fe936cb63 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -930,7 +930,6 @@ func (s *MethodTestSuite) TestTemplate() { }).Asserts(t1, rbac.ActionUpdate).Returns() })) s.Run("GetTemplateInsights", s.Subtest(func(db database.Store, check *expects) { - //tpl := dbgen.Template(s.T(), db, database.Template{}) check.Args(database.GetTemplateInsightsParams{}).Asserts(rbac.ResourceTemplateInsights, rbac.ActionRead) })) s.Run("GetUserLatencyInsights", s.Subtest(func(db database.Store, check *expects) {