Skip to content

chore: autogenerate audit log documentation #5862

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jan 26, 2023
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
"tailnet",
"tailnettest",
"Tailscale",
"tbody",
"TCGETS",
"tcpip",
"TCSETS",
Expand All @@ -128,6 +129,7 @@
"tfjson",
"tfplan",
"tfstate",
"thead",
"tios",
"tmpdir",
"tparallel",
Expand Down
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ gen: \
provisionerd/proto/provisionerd.pb.go \
site/src/api/typesGenerated.ts \
docs/admin/prometheus.md \
docs/admin/audit-logs.md \
coderd/apidoc/swagger.json \
.prettierignore.include \
.prettierignore \
Expand All @@ -436,6 +437,7 @@ gen/mark-fresh:
provisionerd/proto/provisionerd.pb.go \
site/src/api/typesGenerated.ts \
docs/admin/prometheus.md \
docs/admin/audit-logs.md \
coderd/apidoc/swagger.json \
.prettierignore.include \
.prettierignore \
Expand Down Expand Up @@ -490,6 +492,11 @@ docs/admin/prometheus.md: scripts/metricsdocgen/main.go scripts/metricsdocgen/me
cd site
yarn run format:write:only ../docs/admin/prometheus.md

docs/admin/audit-logs.md: scripts/auditdocgen/main.go enterprise/audit/table.go
go run scripts/auditdocgen/main.go
cd site
yarn run format:write:only ../docs/admin/audit-logs.md

coderd/apidoc/swagger.json: $(shell find ./scripts/apidocgen $(FIND_EXCLUSIONS) -type f) $(wildcard coderd/*.go) $(wildcard enterprise/coderd/*.go) $(wildcard codersdk/*.go) .swaggo docs/manifest.json
./scripts/apidocgen/generate.sh
yarn run --cwd=site format:write:only ../docs/api ../docs/manifest.json ../coderd/apidoc/swagger.json
Expand Down
26 changes: 17 additions & 9 deletions docs/admin/audit-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@ their deployment.

## Tracked Events

We track **create, update and delete** events for the following resources:

- GitSSHKey
- Template
- TemplateVersion
- Workspace
- WorkspaceBuild
- User
- Group
We track the following resources:

<!-- Code generated by 'make docs/admin/audit-logs.md'. DO NOT EDIT -->

| <b>Resource<b> | |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AuditableGroup | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>members</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>quota_allowance</td><td>true</td></tr></tbody></table> |
| GitSSHKey | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>private_key</td><td>true</td></tr><tr><td>public_key</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Organization | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| OrganizationMember | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>organization_id</td><td>true</td></tr><tr><td>roles</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_id</td><td>true</td></tr></tbody></table> |
| Template | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>active_version_id</td><td>true</td></tr><tr><td>allow_user_cancel_workspace_jobs</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>default_ttl</td><td>true</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>description</td><td>true</td></tr><tr><td>display_name</td><td>true</td></tr><tr><td>group_acl</td><td>true</td></tr><tr><td>icon</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>is_private</td><td>true</td></tr><tr><td>min_autostart_interval</td><td>true</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>provisioner</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>user_acl</td><td>true</td></tr></tbody></table> |
| TemplateVersion | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>created_at</td><td>false</td></tr><tr><td>created_by</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>readme</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| User | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>avatar_url</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>true</td></tr><tr><td>email</td><td>true</td></tr><tr><td>hashed_password</td><td>true</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_seen_at</td><td>false</td></tr><tr><td>login_type</td><td>false</td></tr><tr><td>rbac_roles</td><td>true</td></tr><tr><td>status</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>username</td><td>true</td></tr></tbody></table> |
| Workspace | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>autostart_schedule</td><td>true</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>deleted</td><td>false</td></tr><tr><td>id</td><td>true</td></tr><tr><td>last_used_at</td><td>false</td></tr><tr><td>name</td><td>true</td></tr><tr><td>organization_id</td><td>false</td></tr><tr><td>owner_id</td><td>true</td></tr><tr><td>template_id</td><td>true</td></tr><tr><td>ttl</td><td>true</td></tr><tr><td>updated_at</td><td>false</td></tr></tbody></table> |
| WorkspaceBuild | <table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody><tr><td>build_number</td><td>false</td></tr><tr><td>created_at</td><td>false</td></tr><tr><td>daily_cost</td><td>false</td></tr><tr><td>deadline</td><td>false</td></tr><tr><td>id</td><td>false</td></tr><tr><td>initiator_id</td><td>false</td></tr><tr><td>job_id</td><td>false</td></tr><tr><td>provisioner_state</td><td>false</td></tr><tr><td>reason</td><td>false</td></tr><tr><td>template_version_id</td><td>true</td></tr><tr><td>transition</td><td>false</td></tr><tr><td>updated_at</td><td>false</td></tr><tr><td>workspace_id</td><td>false</td></tr></tbody></table> |

<!-- End generated by 'make docs/admin/audit-logs.md'. -->

## Filtering logs

Expand Down
2 changes: 1 addition & 1 deletion enterprise/audit/generate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ PROJECT_ROOT=$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel)

(
cd "$PROJECT_ROOT"
go run ./scripts/auditgen ./coderd/database "$@"
go run ./scripts/audittypegen ./coderd/database "$@"
)
151 changes: 151 additions & 0 deletions scripts/auditdocgen/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package main

import (
"bytes"
"flag"
"log"
"os"
"sort"
"strconv"
"strings"

"golang.org/x/xerrors"

"github.com/coder/coder/enterprise/audit"
)

var (
auditDocFile string
dryRun bool

generatorPrefix = []byte("<!-- Code generated by 'make docs/admin/audit-logs.md'. DO NOT EDIT -->")
generatorSuffix = []byte("<!-- End generated by 'make docs/admin/audit-logs.md'. -->")
)

/*
*
AuditableResourcesMap is derived from audit.AuditableResources
and has the following structure:

{
friendlyResourceName: {
fieldName1: isTracked,
fieldName2: isTracked,
...
},
...
}
*/
type AuditableResourcesMap map[string]map[string]bool

func main() {
flag.StringVar(&auditDocFile, "audit-doc-file", "docs/admin/audit-logs.md", "Path to audit log doc file")
flag.BoolVar(&dryRun, "dry-run", false, "Dry run")
flag.Parse()

auditableResourcesMap := readAuditableResources()

doc, err := readAuditDoc()
if err != nil {
log.Fatal("can't read audit doc: ", err)
}

doc, err = updateAuditDoc(doc, auditableResourcesMap)
if err != nil {
log.Fatal("can't update audit doc: ", err)
}

if dryRun {
log.Println(string(doc))
return
}

err = writeAuditDoc(doc)
if err != nil {
log.Fatal("can't write updated audit doc: ", err)
}
}

// Transforms audit.AuditableResources to AuditableResourcesMap,
// which uses friendlier language.
func readAuditableResources() AuditableResourcesMap {
auditableResourcesMap := make(AuditableResourcesMap)

for resourceName, resourceFields := range audit.AuditableResources {
friendlyResourceName := strings.Split(resourceName, ".")[2]
fieldNameMap := make(map[string]bool)
for fieldName, action := range resourceFields {
fieldNameMap[fieldName] = action != audit.ActionIgnore
auditableResourcesMap[friendlyResourceName] = fieldNameMap
}
}

return auditableResourcesMap
}

func readAuditDoc() ([]byte, error) {
doc, err := os.ReadFile(auditDocFile)
if err != nil {
return nil, err
}

return doc, nil
}

// Writes a markdown table of audit log resources to a buffer
func updateAuditDoc(doc []byte, auditableResourcesMap AuditableResourcesMap) ([]byte, error) {
// We must sort the resources to ensure table ordering
sortedResourceNames := sortKeys(auditableResourcesMap)

i := bytes.Index(doc, generatorPrefix)
if i < 0 {
return nil, xerrors.New("generator prefix tag not found")
}
tableStartIndex := i + len(generatorPrefix) + 1

j := bytes.Index(doc[tableStartIndex:], generatorSuffix)
if j < 0 {
return nil, xerrors.New("generator suffix tag not found")
}
tableEndIndex := tableStartIndex + j

var buffer bytes.Buffer
buffer.Write(doc[:tableStartIndex])
buffer.WriteByte('\n')

buffer.WriteString("|<b>Resource<b>||\n")
buffer.WriteString("|--|-----------------|\n")

for _, resourceName := range sortedResourceNames {
buffer.WriteString("|" + resourceName + "|<table><thead><tr><th>Field</th><th>Tracked</th></tr></thead><tbody>")

// We must sort the field names to ensure sub-table ordering
sortedFieldNames := sortKeys(auditableResourcesMap[resourceName])

for _, fieldName := range sortedFieldNames {
isTracked := auditableResourcesMap[resourceName][fieldName]
buffer.WriteString("<tr><td>" + fieldName + "</td><td>" + strconv.FormatBool(isTracked) + "</td></tr>")
}

buffer.WriteString("</tbody></table>\n")
}

buffer.WriteString("\n")
buffer.Write(doc[tableEndIndex:])
return buffer.Bytes(), nil
}

func writeAuditDoc(doc []byte) error {
// G306: Expect WriteFile permissions to be 0600 or less
/* #nosec G306 */
return os.WriteFile(auditDocFile, doc, 0644)
}

func sortKeys[T any](stringMap map[string]T) []string {
var keyNames []string
for key := range stringMap {
keyNames = append(keyNames, key)
}
sort.Strings(keyNames)
return keyNames
}
File renamed without changes.
2 changes: 1 addition & 1 deletion scripts/check_enterprise_imports.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
cdroot

set +e
find . -regex ".*\.go" | grep -v "./enterprise" | xargs grep -n "github.com/coder/coder/enterprise"
find . -regex ".*\.go" | grep -v "./enterprise" | grep -v "./scripts/auditdocgen/main.go" | xargs grep -n "github.com/coder/coder/enterprise"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grep -v "./scripts/auditdocgen/*.go" - in case we add more .go files in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! I'll add this change in this PR.

# reverse the exit code because we want this script to fail if grep finds anything.
status=$?
set -e
Expand Down