github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/core/auditlog/auditlog_test.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package auditlog_test 5 6 import ( 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/juju/clock/testclock" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/core/auditlog" 17 "github.com/juju/juju/core/paths" 18 ) 19 20 type AuditLogSuite struct { 21 testing.IsolationSuite 22 } 23 24 var _ = gc.Suite(&AuditLogSuite{}) 25 26 func (s *AuditLogSuite) TestAuditLogFile(c *gc.C) { 27 dir := c.MkDir() 28 logFile := auditlog.NewLogFile(dir, 300, 10) 29 err := logFile.AddConversation(auditlog.Conversation{ 30 Who: "deerhoof", 31 What: "gojira", 32 When: "2017-11-27T13:21:24Z", 33 ModelName: "admin/default", 34 ConversationID: "0123456789abcdef", 35 ConnectionID: "AC1", 36 }) 37 c.Assert(err, jc.ErrorIsNil) 38 err = logFile.AddRequest(auditlog.Request{ 39 ConversationID: "0123456789abcdef", 40 ConnectionID: "AC1", 41 RequestID: 25, 42 When: "2017-12-12T11:34:56Z", 43 Facade: "Application", 44 Method: "Deploy", 45 Version: 4, 46 Args: `{"applications": [{"application": "prometheus"}]}`, 47 }) 48 c.Assert(err, jc.ErrorIsNil) 49 err = logFile.AddResponse(auditlog.ResponseErrors{ 50 ConversationID: "0123456789abcdef", 51 ConnectionID: "AC1", 52 RequestID: 25, 53 When: "2017-12-12T11:35:11Z", 54 Errors: []*auditlog.Error{ 55 {Message: "oops", Code: "unauthorized access"}, 56 }, 57 }) 58 c.Assert(err, jc.ErrorIsNil) 59 err = logFile.Close() 60 c.Assert(err, jc.ErrorIsNil) 61 62 bytes, err := os.ReadFile(filepath.Join(dir, "audit.log")) 63 c.Assert(err, jc.ErrorIsNil) 64 c.Assert(string(bytes), gc.Equals, expectedLogContents) 65 } 66 67 func (s *AuditLogSuite) TestAuditLogFilePriming(c *gc.C) { 68 dir := c.MkDir() 69 logFile := auditlog.NewLogFile(dir, 300, 10) 70 err := logFile.Close() 71 c.Assert(err, jc.ErrorIsNil) 72 73 info, err := os.Stat(filepath.Join(dir, "audit.log")) 74 c.Assert(err, jc.ErrorIsNil) 75 c.Assert(info.Mode(), gc.Equals, paths.LogfilePermission) 76 // The chown will only work when run as root. 77 } 78 79 func (s *AuditLogSuite) TestRecorder(c *gc.C) { 80 var log fakeLog 81 logTime, err := time.Parse(time.RFC3339, "2017-11-27T15:45:23Z") 82 c.Assert(err, jc.ErrorIsNil) 83 clock := testclock.NewClock(logTime) 84 rec, err := auditlog.NewRecorder(&log, clock, auditlog.ConversationArgs{ 85 Who: "wildbirds and peacedrums", 86 What: "Doubt/Hope", 87 ModelName: "admin/default", 88 ConnectionID: 687, 89 }) 90 c.Assert(err, jc.ErrorIsNil) 91 clock.Advance(time.Second) 92 err = rec.AddRequest(auditlog.RequestArgs{ 93 RequestID: 246, 94 Facade: "Death Vessel", 95 Method: "Horchata", 96 Version: 5, 97 Args: `{"a": "something"}`, 98 }) 99 c.Assert(err, jc.ErrorIsNil) 100 clock.Advance(time.Second) 101 err = rec.AddResponse(auditlog.ResponseErrorsArgs{ 102 RequestID: 246, 103 Errors: []*auditlog.Error{{ 104 Message: "something bad", 105 Code: "bad request", 106 }}, 107 }) 108 c.Assert(err, jc.ErrorIsNil) 109 110 log.stub.CheckCallNames(c, "AddConversation", "AddRequest", "AddResponse") 111 calls := log.stub.Calls() 112 rec0 := calls[0].Args[0].(auditlog.Conversation) 113 callID := rec0.ConversationID 114 c.Assert(rec0, gc.DeepEquals, auditlog.Conversation{ 115 Who: "wildbirds and peacedrums", 116 What: "Doubt/Hope", 117 When: "2017-11-27T15:45:23Z", 118 ModelName: "admin/default", 119 ConnectionID: "2AF", 120 ConversationID: callID, 121 }) 122 c.Assert(calls[1].Args[0], gc.DeepEquals, auditlog.Request{ 123 ConversationID: callID, 124 ConnectionID: "2AF", 125 RequestID: 246, 126 When: "2017-11-27T15:45:24Z", 127 Facade: "Death Vessel", 128 Method: "Horchata", 129 Version: 5, 130 Args: `{"a": "something"}`, 131 }) 132 c.Assert(calls[2].Args[0], gc.DeepEquals, auditlog.ResponseErrors{ 133 ConversationID: callID, 134 ConnectionID: "2AF", 135 RequestID: 246, 136 When: "2017-11-27T15:45:25Z", 137 Errors: []*auditlog.Error{{ 138 Message: "something bad", 139 Code: "bad request", 140 }}, 141 }) 142 } 143 144 type fakeLog struct { 145 stub testing.Stub 146 } 147 148 func (l *fakeLog) AddConversation(m auditlog.Conversation) error { 149 l.stub.AddCall("AddConversation", m) 150 return l.stub.NextErr() 151 } 152 153 func (l *fakeLog) AddRequest(m auditlog.Request) error { 154 l.stub.AddCall("AddRequest", m) 155 return l.stub.NextErr() 156 } 157 158 func (l *fakeLog) AddResponse(m auditlog.ResponseErrors) error { 159 l.stub.AddCall("AddResponse", m) 160 return l.stub.NextErr() 161 } 162 163 func (l *fakeLog) Close() error { 164 l.stub.AddCall("Close") 165 return l.stub.NextErr() 166 } 167 168 var ( 169 expectedLogContents = ` 170 {"conversation":{"who":"deerhoof","what":"gojira","when":"2017-11-27T13:21:24Z","model-name":"admin/default","model-uuid":"","conversation-id":"0123456789abcdef","connection-id":"AC1"}} 171 {"request":{"conversation-id":"0123456789abcdef","connection-id":"AC1","request-id":25,"when":"2017-12-12T11:34:56Z","facade":"Application","method":"Deploy","version":4,"args":"{\"applications\": [{\"application\": \"prometheus\"}]}"}} 172 {"errors":{"conversation-id":"0123456789abcdef","connection-id":"AC1","request-id":25,"when":"2017-12-12T11:35:11Z","errors":[{"message":"oops","code":"unauthorized access"}]}} 173 `[1:] 174 )