Skip to content

Commit

Permalink
add attachment events
Browse files Browse the repository at this point in the history
  • Loading branch information
torcolvin committed Jul 18, 2024
1 parent 2fddace commit 600d052
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 8 deletions.
10 changes: 5 additions & 5 deletions base/audit_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,7 +949,7 @@ var AuditEvents = events{
AuditFieldPurged: true,
},
mandatoryFieldGroups: []fieldGroup{
fieldGroupAuthenticated,
// fieldGroupAuthenticated, // FIXME: CBG-3973
fieldGroupDatabase,
fieldGroupKeyspace,
},
Expand All @@ -966,7 +966,7 @@ var AuditEvents = events{
AuditFieldAttachmentID: "attachment name",
},
mandatoryFieldGroups: []fieldGroup{
fieldGroupAuthenticated,
// fieldGroupAuthenticated, FIXME: CBG-3973
fieldGroupDatabase,
fieldGroupKeyspace,
fieldGroupRequest,
Expand All @@ -986,7 +986,7 @@ var AuditEvents = events{
AuditFieldAttachmentID: "attachment name",
},
mandatoryFieldGroups: []fieldGroup{
fieldGroupAuthenticated,
// fieldGroupAuthenticated, FIXME: CBG-3973
fieldGroupDatabase,
fieldGroupKeyspace,
fieldGroupRequest,
Expand All @@ -1004,7 +1004,7 @@ var AuditEvents = events{
AuditFieldAttachmentID: "attachment name",
},
mandatoryFieldGroups: []fieldGroup{
fieldGroupAuthenticated,
// fieldGroupAuthenticated, // FIXME: CBG-3973
fieldGroupDatabase,
fieldGroupKeyspace,
fieldGroupRequest,
Expand All @@ -1022,7 +1022,7 @@ var AuditEvents = events{
AuditFieldAttachmentID: "attachment name",
},
mandatoryFieldGroups: []fieldGroup{
fieldGroupAuthenticated,
// fieldGroupAuthenticated, // FIXME: CBG-3973
fieldGroupDatabase,
fieldGroupKeyspace,
fieldGroupRequest,
Expand Down
135 changes: 133 additions & 2 deletions rest/audit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,13 +581,128 @@ func TestAuditDocumentRead(t *testing.T) {
RequireStatus(t, resp, http.StatusOK)
})
requireDocumentReadEvents(rt, output, testCase.docID, testCase.docReadVersions)
requireDocumentMetadataReadEvents(rt, output, testCase.docID, testCase.docMetadataReadCount)
requireDocumentMetadataReadEvents(rt, output, testCase.docID, docVersion.RevID, testCase.docMetadataReadCount)
})
}
}

func TestAuditAttachmentEvents(t *testing.T) {
rt := createAuditLoggingRestTester(t)
defer rt.Close()

RequireStatus(t, rt.CreateDatabase("db", rt.NewDbConfig()), http.StatusCreated)
const noAttachmentDoc = "doc1"
noAttachmentDocVersion := rt.CreateTestDoc(noAttachmentDoc)

const hasAttachmentDoc = "doc2"
hasAttachmentDocVersion := rt.CreateTestDoc(hasAttachmentDoc)
rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/doc2/attachment1?rev="+hasAttachmentDocVersion.RevID, "contentdoc2")
hasAttachmentDocVersion, _ = rt.GetDoc(hasAttachmentDoc)

const willUpdateAttachmentDoc = "doc3"
willUpdateAttachmentDocVersion := rt.CreateTestDoc(willUpdateAttachmentDoc)
rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/doc3/attachment1?rev="+willUpdateAttachmentDocVersion.RevID, "contentdoc3")
willUpdateAttachmentDocVersion, _ = rt.GetDoc(willUpdateAttachmentDoc)

const bulkWillUpdateAttachmentDoc = "doc4"
bulkWillUpdateAttachmentDocVersion := rt.CreateTestDoc(bulkWillUpdateAttachmentDoc)
rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/doc4/attachment1?rev="+bulkWillUpdateAttachmentDocVersion.RevID, "contentdoc4")
bulkWillUpdateAttachmentDocVersion, _ = rt.GetDoc(bulkWillUpdateAttachmentDoc)

const willDeleteAttachmentDoc = "doc5"
willDeleteAttachmentDocVersion := rt.CreateTestDoc(willDeleteAttachmentDoc)
rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/doc5/attachment1?rev="+willDeleteAttachmentDocVersion.RevID, "contentdoc5")
willDeleteAttachmentDocVersion, _ = rt.GetDoc(willDeleteAttachmentDoc)

const bulkWillDeleteAttachmentDoc = "doc6"
bulkWillDeleteAttachmentDocVersion := rt.CreateTestDoc(willDeleteAttachmentDoc)
rt.SendAdminRequest(http.MethodPut, "/{{.keyspace}}/doc6/attachment1?rev="+bulkWillDeleteAttachmentDocVersion.RevID, "contentdoc5")
bulkWillDeleteAttachmentDocVersion, _ = rt.GetDoc(bulkWillDeleteAttachmentDoc)

testCases := []struct {
name string
method string
path string
docID string
requestBody string
status int
attachmentCreateCount int
attachmentReadCount int
attachmentUpdateCount int
attachmentDeleteCount int
}{
{
name: "add attachment",
method: http.MethodPut,
path: "/{{.keyspace}}/doc1/attachment1?rev=" + noAttachmentDocVersion.RevID,
docID: noAttachmentDoc,
status: http.StatusCreated,
requestBody: "content",
attachmentCreateCount: 1,
},
{
name: "get attachment with rev",
method: http.MethodGet,
path: "/{{.keyspace}}/doc2/attachment1?rev=" + hasAttachmentDocVersion.RevID,
docID: hasAttachmentDoc,
status: http.StatusOK,
attachmentReadCount: 1,
},
{
name: "_bulk_get attachment with rev",
method: http.MethodPost,
path: "/{{.keyspace}}/_bulk_get?attachments=true",
requestBody: string(base.MustJSONMarshal(t, db.Body{
"docs": []db.Body{
{"id": hasAttachmentDoc, "rev": hasAttachmentDocVersion.RevID},
},
})),
docID: hasAttachmentDoc,
status: http.StatusOK,
attachmentReadCount: 1,
},
{
name: "_all_docs attachment with rev",
method: http.MethodGet,
path: "/{{.keyspace}}/_all_docs?include_docs=true",
docID: hasAttachmentDoc,
status: http.StatusOK,
},
{
name: "update attachment",
method: http.MethodPut,
path: "/{{.keyspace}}/doc3/attachment1?rev=" + hasAttachmentDocVersion.RevID,
docID: willUpdateAttachmentDoc,
status: http.StatusCreated,
requestBody: "content-update",
attachmentUpdateCount: 1,
},
{
name: "delete attachment",
method: http.MethodDelete,
path: "/{{.keyspace}}/doc4/attachment1?rev=" + willDeleteAttachmentDocVersion.RevID,
docID: willDeleteAttachmentDoc,
status: http.StatusOK,
attachmentDeleteCount: 1,
},
}
for _, testCase := range testCases {
rt.Run(testCase.name, func(t *testing.T) {
output := base.AuditLogContents(t, func(t testing.TB) {
resp := rt.SendAdminRequest(testCase.method, testCase.path, testCase.requestBody)
RequireStatus(t, resp, testCase.status)
})
postAttachmentVersion, _ := rt.GetDoc(testCase.docID)
requireAttachmentEvents(rt, base.AuditIDAttachmentCreate, output, testCase.docID, postAttachmentVersion.RevID, testCase.attachmentCreateCount)
requireAttachmentEvents(rt, base.AuditIDAttachmentRead, output, testCase.docID, postAttachmentVersion.RevID, testCase.attachmentReadCount)
requireAttachmentEvents(rt, base.AuditIDAttachmentUpdate, output, testCase.docID, postAttachmentVersion.RevID, testCase.attachmentUpdateCount)
requireAttachmentEvents(rt, base.AuditIDAttachmentDelete, output, testCase.docID, postAttachmentVersion.RevID, testCase.attachmentDeleteCount)
})
}
}

// requireDocumentMetadataReadEvents validates that there read events for each doc version specified. There should be only audit events for a given docid.
func requireDocumentMetadataReadEvents(rt *RestTester, output []byte, docID string, count int) {
func requireDocumentMetadataReadEvents(rt *RestTester, output []byte, docID string, revid string, count int) {
events := jsonLines(rt.TB(), output)
countFound := 0
for _, event := range events {
Expand Down Expand Up @@ -617,6 +732,22 @@ func requireDocumentReadEvents(rt *RestTester, output []byte, docID string, docV
require.Equal(rt.TB(), docVersions, docVersionsFound)
}

// requireAttachmentEvents validates that create attachment events
func requireAttachmentEvents(rt *RestTester, eventID base.AuditID, output []byte, docID, docVersion string, count int) {
events := jsonLines(rt.TB(), output)
countFound := 0
for _, event := range events {
// skip events that are not document read events
if base.AuditID(event[base.AuditFieldID].(float64)) != eventID {
continue
}
require.Equal(rt.TB(), event[base.AuditFieldDocID], docID)
require.Equal(rt.TB(), docVersion, event[base.AuditFieldDocVersion].(string))
countFound++
}
require.Equal(rt.TB(), count, countFound)
}

func createAuditLoggingRestTester(t *testing.T) *RestTester {
// get tempdir before resetting global loggers, since the logger cleanup needs to happen before deletion
tempdir := t.TempDir()
Expand Down
14 changes: 13 additions & 1 deletion rest/bulk_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ func (h *handler) handleAllDocs() error {
}
totalRows++
var err error
fmt.Printf("row: %+v value=%+v\n", row, row.Value)
err = h.addJSON(row)
if err != nil {
return false, err
Expand Down Expand Up @@ -458,6 +457,19 @@ func (h *handler) handleBulkGet() error {
base.AuditFieldDocID: docid,
base.AuditFieldDocVersion: revid,
})
if atts, ok := body[db.BodyAttachments]; ok && atts != nil {
attsMap, ok := atts.(db.AttachmentsMeta)
if !ok {
base.WarnfCtx(h.ctx(), "Unexpected format of attachments in the body %+v", atts)
}
for attachment := range attsMap {
base.Audit(h.ctx(), base.AuditIDAttachmentRead, base.AuditFields{
base.AuditFieldDocID: docid,
base.AuditFieldDocVersion: revid,
base.AuditFieldAttachmentID: attachment,
})
}
}
h.db.DbStats.Database().NumDocReadsRest.Add(1)
}
return nil
Expand Down
23 changes: 23 additions & 0 deletions rest/doc_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,11 @@ func (h *handler) handleGetAttachment() error {
h.db.DbStats.CBLReplicationPull().AttachmentPullBytes.Add(int64(len(data)))
h.response.WriteHeader(status)
_, _ = h.response.Write(data)
base.Audit(h.ctx(), base.AuditIDAttachmentRead, base.AuditFields{
base.AuditFieldDocID: docid,
base.AuditFieldDocVersion: revid,
base.AuditFieldAttachmentID: attachmentName,
})
return nil

}
Expand Down Expand Up @@ -349,10 +354,13 @@ func (h *handler) handlePutAttachment() error {
}
}

attachmentUpdated := false
// find attachment (if it existed)
attachments := db.GetBodyAttachments(body)
if attachments == nil {
attachments = make(map[string]interface{})
} else {
_, attachmentUpdated = attachments[attachmentName]
}

// create new attachment
Expand All @@ -371,6 +379,16 @@ func (h *handler) handlePutAttachment() error {
h.setEtag(newRev)

h.writeRawJSONStatus(http.StatusCreated, []byte(`{"id":`+base.ConvertToJSONString(docid)+`,"ok":true,"rev":"`+newRev+`"}`))
auditFields := base.AuditFields{
base.AuditFieldDocID: docid,
base.AuditFieldDocVersion: newRev,
base.AuditFieldAttachmentID: attachmentName,
}
if attachmentUpdated {
base.Audit(h.ctx(), base.AuditIDAttachmentUpdate, auditFields)
} else {
base.Audit(h.ctx(), base.AuditIDAttachmentCreate, auditFields)
}
return nil
}

Expand Down Expand Up @@ -426,6 +444,11 @@ func (h *handler) handleDeleteAttachment() error {
h.setEtag(newRev)

h.writeRawJSONStatus(http.StatusOK, []byte(`{"id":`+base.ConvertToJSONString(docid)+`,"ok":true,"rev":"`+newRev+`"}`))
base.Audit(h.ctx(), base.AuditIDAttachmentDelete, base.AuditFields{
base.AuditFieldDocID: docid,
base.AuditFieldDocVersion: newRev,
base.AuditFieldAttachmentID: attachmentName,
})

return nil
}
Expand Down

0 comments on commit 600d052

Please sign in to comment.