@@ -12,13 +12,19 @@ import (
12
12
"time"
13
13
14
14
"github.com/google/uuid"
15
+ "github.com/prometheus/client_golang/prometheus"
15
16
"github.com/stretchr/testify/require"
16
17
18
+ "cdr.dev/slog/sloggers/slogtest"
19
+ "github.com/coder/coder/v2/coderd/coderdtest"
17
20
"github.com/coder/coder/v2/coderd/database"
18
21
"github.com/coder/coder/v2/coderd/database/db2sdk"
22
+ "github.com/coder/coder/v2/coderd/database/dbauthz"
19
23
"github.com/coder/coder/v2/coderd/database/dbgen"
24
+ "github.com/coder/coder/v2/coderd/database/dbtestutil"
20
25
"github.com/coder/coder/v2/coderd/database/dbtime"
21
26
"github.com/coder/coder/v2/coderd/database/migrations"
27
+ "github.com/coder/coder/v2/coderd/rbac"
22
28
"github.com/coder/coder/v2/testutil"
23
29
)
24
30
@@ -767,6 +773,143 @@ func TestReadCustomRoles(t *testing.T) {
767
773
}
768
774
}
769
775
776
+ func TestAuthorizedAuditLogs (t * testing.T ) {
777
+ t .Parallel ()
778
+
779
+ var allLogs []database.AuditLog
780
+ db , _ := dbtestutil .NewDB (t )
781
+ authz := rbac .NewAuthorizer (prometheus .NewRegistry ())
782
+ db = dbauthz .New (db , authz , slogtest .Make (t , & slogtest.Options {}), coderdtest .AccessControlStorePointer ())
783
+
784
+ siteWideIDs := []uuid.UUID {uuid .New (), uuid .New ()}
785
+ for _ , id := range siteWideIDs {
786
+ allLogs = append (allLogs , dbgen .AuditLog (t , db , database.AuditLog {
787
+ ID : id ,
788
+ OrganizationID : uuid .Nil ,
789
+ }))
790
+
791
+ }
792
+
793
+ orgAuditLogs := map [uuid.UUID ][]uuid.UUID {
794
+ uuid .New (): {uuid .New (), uuid .New ()},
795
+ uuid .New (): {uuid .New (), uuid .New ()},
796
+ }
797
+ orgIDs := make ([]uuid.UUID , 0 , len (orgAuditLogs ))
798
+ for orgID := range orgAuditLogs {
799
+ orgIDs = append (orgIDs , orgID )
800
+ }
801
+ for orgID , ids := range orgAuditLogs {
802
+ dbgen .Organization (t , db , database.Organization {
803
+ ID : orgID ,
804
+ })
805
+ for _ , id := range ids {
806
+ allLogs = append (allLogs , dbgen .AuditLog (t , db , database.AuditLog {
807
+ ID : id ,
808
+ OrganizationID : orgID ,
809
+ }))
810
+ }
811
+ }
812
+
813
+ // Now fetch all the logs
814
+ ctx := testutil .Context (t , testutil .WaitLong )
815
+ auditorRole , err := rbac .RoleByName (rbac .RoleAuditor ())
816
+ require .NoError (t , err )
817
+
818
+ memberRole , err := rbac .RoleByName (rbac .RoleMember ())
819
+ require .NoError (t , err )
820
+
821
+ orgAuditorRoles := func (t * testing.T , orgID uuid.UUID ) rbac.Role {
822
+ t .Helper ()
823
+
824
+ role , err := rbac .RoleByName (rbac .ScopedRoleOrgAuditor (orgID ))
825
+ require .NoError (t , err )
826
+ return role
827
+ }
828
+
829
+ t .Run ("NoAccess" , func (t * testing.T ) {
830
+ siteAuditorCtx := dbauthz .As (ctx , rbac.Subject {
831
+ FriendlyName : "member" ,
832
+ ID : uuid .NewString (),
833
+ Roles : rbac.Roles {memberRole },
834
+ Scope : rbac .ScopeAll ,
835
+ })
836
+
837
+ logs , err := db .GetAuditLogsOffset (siteAuditorCtx , database.GetAuditLogsOffsetParams {})
838
+ require .NoError (t , err )
839
+ require .Len (t , logs , 0 , "no logs should be returned" )
840
+ })
841
+
842
+ t .Run ("SiteWideAuditor" , func (t * testing.T ) {
843
+ siteAuditorCtx := dbauthz .As (ctx , rbac.Subject {
844
+ FriendlyName : "owner" ,
845
+ ID : uuid .NewString (),
846
+ Roles : rbac.Roles {auditorRole },
847
+ Scope : rbac .ScopeAll ,
848
+ })
849
+
850
+ logs , err := db .GetAuditLogsOffset (siteAuditorCtx , database.GetAuditLogsOffsetParams {})
851
+ require .NoError (t , err )
852
+ require .ElementsMatch (t , auditOnlyIDs (allLogs ), auditOnlyIDs (logs ))
853
+ })
854
+
855
+ t .Run ("SingleOrgAuditor" , func (t * testing.T ) {
856
+ orgID := orgIDs [0 ]
857
+ siteAuditorCtx := dbauthz .As (ctx , rbac.Subject {
858
+ FriendlyName : "org-auditor" ,
859
+ ID : uuid .NewString (),
860
+ Roles : rbac.Roles {orgAuditorRoles (t , orgID )},
861
+ Scope : rbac .ScopeAll ,
862
+ })
863
+
864
+ logs , err := db .GetAuditLogsOffset (siteAuditorCtx , database.GetAuditLogsOffsetParams {})
865
+ require .NoError (t , err )
866
+ require .ElementsMatch (t , orgAuditLogs [orgID ], auditOnlyIDs (logs ))
867
+ })
868
+
869
+ t .Run ("TwoOrgAuditors" , func (t * testing.T ) {
870
+ first := orgIDs [0 ]
871
+ second := orgIDs [1 ]
872
+ siteAuditorCtx := dbauthz .As (ctx , rbac.Subject {
873
+ FriendlyName : "org-auditor" ,
874
+ ID : uuid .NewString (),
875
+ Roles : rbac.Roles {orgAuditorRoles (t , first ), orgAuditorRoles (t , second )},
876
+ Scope : rbac .ScopeAll ,
877
+ })
878
+
879
+ logs , err := db .GetAuditLogsOffset (siteAuditorCtx , database.GetAuditLogsOffsetParams {})
880
+ require .NoError (t , err )
881
+ require .ElementsMatch (t , append (orgAuditLogs [first ], orgAuditLogs [second ]... ), auditOnlyIDs (logs ))
882
+ })
883
+
884
+ t .Run ("ErroneousOrg" , func (t * testing.T ) {
885
+ siteAuditorCtx := dbauthz .As (ctx , rbac.Subject {
886
+ FriendlyName : "org-auditor" ,
887
+ ID : uuid .NewString (),
888
+ Roles : rbac.Roles {orgAuditorRoles (t , uuid .New ())},
889
+ Scope : rbac .ScopeAll ,
890
+ })
891
+
892
+ logs , err := db .GetAuditLogsOffset (siteAuditorCtx , database.GetAuditLogsOffsetParams {})
893
+ require .NoError (t , err )
894
+ require .Len (t , logs , 0 , "no logs should be returned" )
895
+ })
896
+ }
897
+
898
+ func auditOnlyIDs [T database.AuditLog | database.GetAuditLogsOffsetRow ](logs []T ) []uuid.UUID {
899
+ ids := make ([]uuid.UUID , 0 , len (logs ))
900
+ for _ , log := range logs {
901
+ switch log := any (log ).(type ) {
902
+ case database.AuditLog :
903
+ ids = append (ids , log .ID )
904
+ case database.GetAuditLogsOffsetRow :
905
+ ids = append (ids , log .AuditLog .ID )
906
+ default :
907
+ panic ("unreachable" )
908
+ }
909
+ }
910
+ return ids
911
+ }
912
+
770
913
type tvArgs struct {
771
914
Status database.ProvisionerJobStatus
772
915
// CreateWorkspace is true if we should create a workspace for the template version
0 commit comments